添加日志记录

This commit is contained in:
tursom 2019-12-15 15:30:33 +08:00
parent 48ba8855bc
commit 560eddac2a
14 changed files with 189 additions and 45 deletions

View File

@ -0,0 +1,29 @@
package cn.tursom.log
inline fun lazyLog(crossinline toString: () -> String) = object {
override fun toString(): String = toString()
}
@Suppress("NOTHING_TO_INLINE")
inline fun lazyPrettyMap(map: Iterator<Map.Entry<CharSequence, Any>>) = lazyLog {
val sb = StringBuilder("{\n")
map.forEach { (k, v) ->
sb.append(" $k: $v\n")
}
sb.append("}")
sb.toString()
}
@Suppress("NOTHING_TO_INLINE")
inline fun lazyPrettyMap(map: Iterable<Map.Entry<CharSequence, Any>>) = if (map.iterator().hasNext()) {
lazyPrettyMap(map.iterator())
} else {
"{}"
}
@Suppress("NOTHING_TO_INLINE")
inline fun lazyPrettyMap(map: Map<out CharSequence, Any>) = if (map.isNotEmpty()) {
lazyPrettyMap(map.iterator())
} else {
"{}"
}

View File

@ -1,5 +1,6 @@
dependencies {
implementation project(":")
implementation project(":web")
api project(":log")
implementation group: "io.netty", name: "netty-all", version: "4.1.43.Final"
}

View File

@ -6,6 +6,7 @@ import io.netty.buffer.ByteBufAllocator
import io.netty.buffer.Unpooled
import io.netty.channel.ChannelHandlerContext
import io.netty.handler.stream.ChunkedInput
import org.slf4j.LoggerFactory
class NettyChunkedByteBuffer(private val bufList: List<ByteBuffer>) : ChunkedInput<ByteBuf> {
constructor(vararg bufList: ByteBuffer) : this(bufList.asList())
@ -29,6 +30,7 @@ class NettyChunkedByteBuffer(private val bufList: List<ByteBuffer>) : ChunkedInp
override fun readChunk(allocator: ByteBufAllocator?): ByteBuf = readChunk()
private fun readChunk(): ByteBuf {
log?.trace("readChunk")
this.next?.close()
val next = iterator.next()
this.next = next
@ -37,6 +39,16 @@ class NettyChunkedByteBuffer(private val bufList: List<ByteBuffer>) : ChunkedInp
else Unpooled.wrappedBuffer(next.readBuffer())
}
override fun close() {}
override fun close() {
log?.trace("close")
}
companion object {
private val log = try {
LoggerFactory.getLogger(NettyChunkedByteBuffer::class.java)
} catch (e: Throwable) {
null
}
}
}

View File

@ -6,23 +6,33 @@ import io.netty.buffer.ByteBufAllocator
import io.netty.buffer.Unpooled
import io.netty.channel.ChannelHandlerContext
import io.netty.handler.stream.ChunkedInput
import org.slf4j.LoggerFactory
class NettyChunkedInput(val chunked: Chunked) : ChunkedInput<ByteBuf> {
override fun progress(): Long = chunked.progress
override fun length() = chunked.length
override fun isEndOfInput(): Boolean = chunked.endOfInput
class NettyChunkedInput(private val chunked: Chunked) : ChunkedInput<ByteBuf> {
override fun progress(): Long = chunked.progress
override fun length() = chunked.length
override fun isEndOfInput(): Boolean = chunked.endOfInput
override fun readChunk(ctx: ChannelHandlerContext?): ByteBuf {
val buf = chunked.readChunk()
return if (buf is NettyByteBuffer) buf.byteBuf
else Unpooled.wrappedBuffer(buf.readBuffer())
}
override fun readChunk(ctx: ChannelHandlerContext?): ByteBuf = readChunk()
override fun readChunk(allocator: ByteBufAllocator?): ByteBuf = readChunk()
@Suppress("MemberVisibilityCanBePrivate")
fun readChunk(): ByteBuf {
val buf = chunked.readChunk()
log?.trace("readChunk {}", buf)
return if (buf is NettyByteBuffer) buf.byteBuf
else Unpooled.wrappedBuffer(buf.readBuffer())
}
override fun readChunk(allocator: ByteBufAllocator?): ByteBuf {
val buf = chunked.readChunk()
return if (buf is NettyByteBuffer) buf.byteBuf
else Unpooled.wrappedBuffer(buf.readBuffer())
}
override fun close() {
log?.trace("close")
chunked.close()
}
override fun close() = chunked.close()
companion object {
private val log = try {
LoggerFactory.getLogger(NettyChunkedInput::class.java)
} catch (e: Throwable) {
null
}
}
}

View File

@ -9,6 +9,7 @@ import io.netty.buffer.Unpooled
import io.netty.channel.ChannelHandlerContext
import io.netty.handler.codec.http.*
import io.netty.handler.stream.ChunkedFile
import org.slf4j.LoggerFactory
import java.io.File
import java.io.RandomAccessFile
import kotlin.collections.set
@ -31,7 +32,7 @@ open class NettyHttpContent(
val httpMethod: HttpMethod get() = msg.method()
val protocolVersion: HttpVersion get() = msg.protocolVersion()
val headers: HttpHeaders get() = msg.headers()
protected val paramMap by lazy { RequestParser.parse(msg) }
protected val paramMap by lazy { ParamParser.parse(msg) }
override val cookieMap by lazy { getHeader("Cookie")?.let { decodeCookie(it) } ?: mapOf() }
override val body = msg.content()?.let { NettyByteBuffer(it) }
@ -48,26 +49,32 @@ open class NettyHttpContent(
val responseBodyBuf: CompositeByteBuf = ctx.alloc().compositeBuffer()!!
override fun getHeader(header: String): String? {
log?.trace("getHeader {}", header)
return headers[header]
}
override fun getHeaders(header: String): List<String> {
log?.trace("getHeaders {}", header)
return headers.getAll(header)
}
override fun getHeaders(): Iterable<Map.Entry<String, String>> {
log?.trace("getHeaders")
return headers
}
override fun getParams(): Map<String, List<String>> {
log?.trace("getParams")
return paramMap
}
override fun getParams(param: String): List<String>? {
log?.trace("getParams {}", param)
return paramMap[param]
}
override fun addParam(key: String, value: String) {
log?.trace("addParam {}: {}", key, value)
if (!paramMap.containsKey(key)) {
paramMap[key] = ArrayList()
}
@ -75,23 +82,27 @@ open class NettyHttpContent(
}
override fun write(message: String) {
log?.trace("write {}", message)
responseBodyBuf.addComponent(Unpooled.wrappedBuffer(message.toByteArray()))
//responseBody.write(message.toByteArray())
}
override fun write(byte: Byte) {
log?.trace("write {}", byte)
val buffer = ctx.alloc().buffer(1).writeByte(byte.toInt())
responseBodyBuf.addComponent(buffer)
//responseBody.write(byte.toInt())
}
override fun write(bytes: ByteArray, offset: Int, size: Int) {
log?.trace("write {}({}:{})", bytes, offset, size)
responseBodyBuf.addComponent(Unpooled.wrappedBuffer(bytes, offset, size))
//responseBody.write(bytes, offset, size)
}
override fun write(buffer: ByteBuffer) {
//buffer.writeTo(responseBody)
log?.trace("write {}", buffer)
responseBodyBuf.addComponent(if (buffer is NettyByteBuffer) {
buffer.byteBuf
} else {
@ -102,18 +113,22 @@ open class NettyHttpContent(
}
override fun reset() {
log?.trace("reset")
responseBodyBuf.clear()
}
override fun finish() {
log?.trace("finish")
finish(responseBodyBuf)
}
override fun finish(buffer: ByteArray, offset: Int, size: Int) {
log?.trace("finish ByteArray[{}]({}:{})", buffer.size, offset, size)
finish(Unpooled.wrappedBuffer(buffer, offset, size))
}
override fun finish(buffer: ByteBuffer) {
log?.trace("finish {}", buffer)
if (buffer is NettyByteBuffer) {
finish(buffer.byteBuf)
} else {
@ -123,11 +138,13 @@ open class NettyHttpContent(
fun finish(buf: ByteBuf) = finish(buf, responseStatus)
fun finish(buf: ByteBuf, responseCode: HttpResponseStatus) {
log?.trace("finish {}: {}", responseCode, buf)
val response = DefaultFullHttpResponse(HttpVersion.HTTP_1_1, responseCode, buf)
finish(response)
}
fun finish(response: FullHttpResponse) {
log?.trace("finish {}", response)
finished = true
val heads = response.headers()
addHeaders(
@ -142,6 +159,7 @@ open class NettyHttpContent(
}
override fun writeChunkedHeader() {
log?.trace("writeChunkedHeader")
val response = DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK)
response.status = if (responseMessage != null) HttpResponseStatus(responseCode, responseMessage)
else responseStatus
@ -157,10 +175,12 @@ open class NettyHttpContent(
}
override fun addChunked(buffer: ByteBuffer) {
log?.trace("addChunked {}", buffer)
chunkedList.add(buffer)
}
override fun finishChunked() {
log?.trace("finishChunked {}", chunkedList)
finished = true
writeChunkedHeader()
val httpChunkWriter = HttpChunkedInput(NettyChunkedByteBuffer(chunkedList))
@ -168,6 +188,7 @@ open class NettyHttpContent(
}
override fun finishChunked(chunked: Chunked) {
log?.trace("finishChunked {}", chunked)
finished = true
writeChunkedHeader()
val httpChunkWriter = HttpChunkedInput(NettyChunkedInput(chunked))
@ -175,15 +196,25 @@ open class NettyHttpContent(
}
override fun finishFile(file: File, chunkSize: Int) {
log?.trace("finishFile {} chunkSize {}", file, chunkSize)
finished = true
writeChunkedHeader()
ctx.writeAndFlush(HttpChunkedInput(ChunkedFile(file, chunkSize)))
}
override fun finishFile(file: RandomAccessFile, offset: Long, length: Long, chunkSize: Int) {
log?.trace("finishFile {}({}:{}) chunkSize {}", file, offset, length, chunkSize)
finished = true
writeChunkedHeader()
ctx.writeAndFlush(HttpChunkedInput(ChunkedFile(file, offset, length, chunkSize)))
}
companion object {
private val log = try {
LoggerFactory.getLogger(NettyHttpContent::class.java)
} catch (e: Throwable) {
null
}
}
}

View File

@ -6,6 +6,7 @@ import io.netty.channel.ChannelHandlerContext
import io.netty.channel.SimpleChannelInboundHandler
import io.netty.handler.codec.http.FullHttpRequest
import io.netty.handler.codec.http.HttpResponseStatus
import org.slf4j.LoggerFactory
@ChannelHandler.Sharable
class NettyHttpHandler(
@ -14,6 +15,7 @@ class NettyHttpHandler(
override fun channelRead0(ctx: ChannelHandlerContext, msg: FullHttpRequest) {
val handlerContext = NettyHttpContent(ctx, msg)
log?.debug("{} {} {}", handlerContext.clientIp, handlerContext.method, handlerContext.uri)
handler.handle(handlerContext)
}
@ -23,6 +25,7 @@ class NettyHttpHandler(
}
override fun exceptionCaught(ctx: ChannelHandlerContext, cause: Throwable) {
log?.error("exception cause on NettyHttpHandler", cause)
val content = NettyExceptionContent(ctx, cause)
handler.exceptionCause(content)
if (!content.finished) {
@ -31,4 +34,12 @@ class NettyHttpHandler(
}
ctx.close()
}
companion object {
private val log = try {
LoggerFactory.getLogger(NettyHttpHandler::class.java)
} catch (e: Throwable) {
null
}
}
}

View File

@ -15,7 +15,9 @@ import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler
import io.netty.handler.stream.ChunkedWriteHandler
import io.netty.handler.timeout.ReadTimeoutHandler
import io.netty.handler.timeout.WriteTimeoutHandler
import org.slf4j.LoggerFactory
@Suppress("unused")
class NettyHttpServer(
override val port: Int,
handler: HttpHandler<NettyHttpContent, NettyExceptionContent>,
@ -47,14 +49,10 @@ class NettyHttpServer(
override fun initChannel(ch: SocketChannel) {
ch.pipeline()
.apply {
if (readTimeout != null) {
addLast(ReadTimeoutHandler(readTimeout))
}
if (readTimeout != null) addLast(ReadTimeoutHandler(readTimeout))
}
.apply {
if (writeTimeout != null) {
addLast(WriteTimeoutHandler(writeTimeout))
}
if (writeTimeout != null) addLast(WriteTimeoutHandler(writeTimeout))
}
.addLast("codec", HttpServerCodec())
.addLast("aggregator", HttpObjectAggregator(bodySize))
@ -77,11 +75,22 @@ class NettyHttpServer(
}
override fun run() {
log?.warn("NettyHttpServer started on port {}", port)
log?.info("try http://localhost:{}/", port)
future.sync()
}
override fun close() {
log?.warn("NettyHttpServer({}) closed", port)
future.cancel(false)
future.channel().close()
}
companion object {
private val log = try {
LoggerFactory.getLogger(NettyHttpServer::class.java)
} catch (e: Throwable) {
null
}
}
}

View File

@ -1,8 +1,12 @@
package cn.tursom.web.netty
import cn.tursom.log.lazyPrettyMap
import cn.tursom.web.ResponseHeaderAdapter
import io.netty.handler.codec.http.HttpHeaders
import java.util.HashMap
import org.slf4j.LoggerFactory
import kotlin.collections.component1
import kotlin.collections.component2
import kotlin.collections.set
@Suppress("MemberVisibilityCanBePrivate")
open class NettyResponseHeaderAdapter : ResponseHeaderAdapter {
@ -10,11 +14,13 @@ open class NettyResponseHeaderAdapter : ResponseHeaderAdapter {
val responseListMap = HashMap<String, ArrayList<Any>>()
override fun setResponseHeader(name: String, value: Any) {
log?.trace("setResponseHeader {}: {}", name, value)
responseMap[name] = value
responseListMap.remove(name)
}
override fun addResponseHeader(name: String, value: Any) {
log?.trace("addResponseHeader {}: {}", name, value)
val list = responseListMap[name] ?: run {
val newList = ArrayList<Any>()
responseListMap[name] = newList
@ -28,6 +34,7 @@ open class NettyResponseHeaderAdapter : ResponseHeaderAdapter {
}
protected fun addHeaders(heads: HttpHeaders, defaultHeaders: Map<out CharSequence, Any>) {
log?.trace("addHeader\nheaders {}\ndefault {}", lazyPrettyMap(heads), lazyPrettyMap(defaultHeaders))
responseListMap.forEach { (t, u) ->
u.forEach {
heads.add(t, it)
@ -44,4 +51,12 @@ open class NettyResponseHeaderAdapter : ResponseHeaderAdapter {
}
}
}
companion object {
private val log = try {
LoggerFactory.getLogger(NettyResponseHeaderAdapter::class.java)
} catch (e: Throwable) {
null
}
}
}

View File

@ -5,12 +5,14 @@ import io.netty.handler.codec.http.HttpMethod
import io.netty.handler.codec.http.QueryStringDecoder
import io.netty.handler.codec.http.multipart.Attribute
import io.netty.handler.codec.http.multipart.HttpPostRequestDecoder
import java.util.HashMap
import java.util.*
import kotlin.collections.ArrayList
import kotlin.collections.set
/**
* HTTP请求参数解析器, 支持GET, POST
*/
object RequestParser {
object ParamParser {
fun parse(fullReq: FullHttpRequest): HashMap<String, List<String>> {
val method = fullReq.method()

View File

@ -3,6 +3,7 @@ package cn.tursom.web
import cn.tursom.core.buffer.ByteBuffer
import cn.tursom.core.urlDecode
import cn.tursom.web.utils.Chunked
import org.slf4j.LoggerFactory
import java.io.File
import java.io.RandomAccessFile
import java.net.SocketAddress
@ -36,6 +37,7 @@ interface HttpContent : ResponseHeaderAdapter, RequestHeaderAdapter {
fun finish()
fun finish(buffer: ByteArray, offset: Int = 0, size: Int = buffer.size - offset)
fun finish(buffer: ByteBuffer) {
log?.trace("buffer {}", buffer)
if (buffer.hasArray) {
finish(buffer.array, buffer.readOffset, buffer.readAllSize())
} else {
@ -48,54 +50,63 @@ interface HttpContent : ResponseHeaderAdapter, RequestHeaderAdapter {
fun finish(code: Int) = finishHtml(code)
fun finishHtml(code: Int = responseCode) {
log?.trace("finishHtml {}", code)
responseHtml()
responseCode = code
finish()
}
fun finishText(code: Int = responseCode) {
log?.trace("finishText {}", code)
responseText()
responseCode = code
finish()
}
fun finishJson(code: Int = responseCode) {
log?.trace("finishJson {}", code)
responseJson()
responseCode = code
finish()
}
fun finishHtml(response: ByteArray, code: Int = responseCode) {
log?.trace("finishHtml {}: {}", code, response)
responseHtml()
responseCode = code
finish(response)
}
fun finishText(response: ByteArray, code: Int = responseCode) {
log?.trace("finishText {}: {}", code, response)
responseText()
responseCode = code
finish(response)
}
fun finishJson(response: ByteArray, code: Int = responseCode) {
log?.trace("finishJson {}: {}", code, response)
responseJson()
responseCode = code
finish(response)
}
fun finishHtml(response: ByteBuffer, code: Int = responseCode) {
log?.trace("finishHtml {}: {}", code, response)
responseHtml()
responseCode = code
finish(response)
}
fun finishText(response: ByteBuffer, code: Int = responseCode) {
log?.trace("finishText {}: {}", code, response)
responseText()
responseCode = code
finish(response)
}
fun finishJson(response: ByteBuffer, code: Int = responseCode) {
log?.trace("finishJson {}: {}", code, response)
responseJson()
responseCode = code
finish(response)
@ -123,18 +134,29 @@ interface HttpContent : ResponseHeaderAdapter, RequestHeaderAdapter {
fun moved(url: String) = permanentlyMoved(url)
fun permanentlyMoved(url: String) {
log?.trace("permanentlyMoved {}", url)
setResponseHeader("Location", url)
finish(301)
}
fun temporaryMoved(url: String) {
log?.trace("temporaryMoved {}", url)
noStore()
setResponseHeader("Location", url)
finish(302)
}
fun finish(msg: String) {
log?.trace("finish {}", msg)
write(msg)
finish()
}
companion object {
private val log = try {
LoggerFactory.getLogger(HttpContent::class.java)
} catch (e: Throwable) {
null
}
}
}

View File

@ -1,6 +1,9 @@
package cn.tursom.web.mapping
import cn.tursom.web.utils.MethodEnum
annotation class Mapping(
vararg val route: String,
val method: String = ""
val method: String = "",
val methodEnum: MethodEnum = MethodEnum.NONE
)

View File

@ -8,13 +8,9 @@ import cn.tursom.web.HttpContent
import cn.tursom.web.HttpHandler
import cn.tursom.web.MutableHttpContent
import cn.tursom.web.mapping.*
import cn.tursom.web.result.Html
import cn.tursom.web.result.Json
import cn.tursom.web.result.Text
import cn.tursom.web.result.*
import cn.tursom.web.router.impl.SimpleRouter
import cn.tursom.web.utils.Chunked
import cn.tursom.web.result.ContextType
import cn.tursom.web.result.NoReturnLog
import org.slf4j.LoggerFactory
import java.io.File
import java.io.RandomAccessFile
@ -56,7 +52,6 @@ open class RoutedHttpHandler(
}
override fun handle(content: HttpContent) {
log?.debug("{} {} {}", content.clientIp, content.method, content.uri)
if (content is MutableHttpContent) {
handle(content, getHandler(content, content.method, content.uri))
} else {
@ -77,6 +72,7 @@ open class RoutedHttpHandler(
}
open fun addRouter(handler: Any) {
log?.info("add router {}", handler)
@Suppress("LeakingThis")
val clazz = handler.javaClass
clazz.methods.forEach { method ->
@ -180,6 +176,7 @@ open class RoutedHttpHandler(
}
open fun deleteRouter(handler: Any) {
log?.info("delete router {}", handler)
@Suppress("LeakingThis")
val clazz = handler.javaClass
clazz.methods.forEach { method ->
@ -217,7 +214,7 @@ open class RoutedHttpHandler(
protected fun getRoutes(annotation: Annotation) = when (annotation) {
is Mapping -> {
annotation.route to getRouter(annotation.method)
annotation.route to getRouter(annotation.method.let { if (it.isEmpty()) annotation.methodEnum.method else it })
}
is GetMapping -> {
annotation.route to getRouter("GET")
@ -322,7 +319,7 @@ open class RoutedHttpHandler(
}
fun finishHtml(result: Any?, content: HttpContent, doLog: Boolean = true) {
if (doLog) log?.debug("{}: finishHtml: {}", content.clientIp, result)
if (doLog) log?.debug("{} finishHtml {}", content.clientIp, result)
result ?: return
when (result) {
is ByteBuffer -> content.finishHtml(result)
@ -339,7 +336,7 @@ open class RoutedHttpHandler(
}
fun finishText(result: Any?, content: HttpContent, doLog: Boolean = true) {
if (doLog) log?.debug("{}: finishText: {}", content.clientIp, result)
if (doLog) log?.debug("{} finishText {}", content.clientIp, result)
result ?: return
when (result) {
is ByteBuffer -> content.finishText(result)
@ -356,7 +353,7 @@ open class RoutedHttpHandler(
}
fun finishJson(result: Any?, content: HttpContent, doLog: Boolean = true) {
if (doLog) log?.debug("{}: finishJson: {}", content.clientIp, result)
if (doLog) log?.debug("{} finishJson {}", content.clientIp, result)
result ?: return
when (result) {
is ByteBuffer -> content.finishJson(result)
@ -371,7 +368,7 @@ open class RoutedHttpHandler(
}
else -> {
val json = json?.toJson(result)
if (doLog) log?.debug("{}: finishJson: generate json: {}", content.clientIp, json)
if (doLog) log?.debug("{} finishJson: generate json {}", content.clientIp, json)
if (json != null) {
content.finishJson(json.toByteArray())
} else {

View File

@ -0,0 +1,7 @@
package cn.tursom.web.utils
enum class MethodEnum(val method: String) {
CONNECT("CONNECT"), DELETE("DELETE"), GET("GET"), HEAD("HEAD"),
OPTIONS("OPTIONS"), PATCH("PATCH"), POST("POST"), PUT("PUT"),
TRACE("TRACE"), NONE("")
}

View File

@ -3,12 +3,8 @@ package cn.tursom.web.router
import cn.tursom.web.HttpContent
import cn.tursom.web.MutableHttpContent
import cn.tursom.web.mapping.*
import cn.tursom.web.result.Html
import cn.tursom.web.result.Json
import cn.tursom.web.result.Text
import cn.tursom.web.result.*
import cn.tursom.web.router.impl.SimpleRouter
import cn.tursom.web.result.ContextType
import cn.tursom.web.result.NoReturnLog
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import org.slf4j.LoggerFactory
@ -26,7 +22,6 @@ open class AsyncRoutedHttpHandler(
protected val asyncRouterMap: HashMap<String, Router<Pair<Any?, suspend (HttpContent) -> Unit>>> = HashMap()
override fun handle(content: HttpContent) {
log?.debug("{} {} {}", content.clientIp, content.method, content.uri)
if (content is MutableHttpContent) {
val handler = getAsyncHandler(content, content.method, content.uri)
if (handler != null) GlobalScope.launch {
@ -143,7 +138,7 @@ open class AsyncRoutedHttpHandler(
protected fun getAsyncRoutes(annotation: Annotation) = when (annotation) {
is Mapping -> {
annotation.route to getAsyncRouter(annotation.method)
annotation.route to getAsyncRouter(annotation.method.let { if (it.isEmpty()) annotation.methodEnum.method else it })
}
is GetMapping -> {
annotation.route to getAsyncRouter("GET")