Clean logging mess

This commit is contained in:
xis 2023-10-09 18:01:08 +02:00
parent f730ac7500
commit 5ef7684719
12 changed files with 92 additions and 120 deletions

View file

@ -5,6 +5,7 @@ import jakarta.servlet.http.HttpServletResponse
import net.schowek.nextclouddlna.nextcloud.content.ContentGroup.*
import net.schowek.nextclouddlna.nextcloud.content.ContentTreeProvider
import net.schowek.nextclouddlna.nextcloud.content.MediaFormat
import net.schowek.nextclouddlna.util.Logging
import org.jupnp.support.model.Protocol
import org.jupnp.support.model.ProtocolInfo
import org.jupnp.support.model.dlna.*
@ -14,7 +15,6 @@ import org.jupnp.support.model.dlna.DLNAConversionIndicator.NONE
import org.jupnp.support.model.dlna.DLNAFlags.*
import org.jupnp.support.model.dlna.DLNAOperations.*
import org.jupnp.support.model.dlna.DLNAProfiles.*
import org.slf4j.LoggerFactory
import org.springframework.core.io.FileSystemResource
import org.springframework.http.HttpStatus
import org.springframework.http.ResponseEntity
@ -25,9 +25,7 @@ import java.util.*
@RestController
class ContentController(
private val contentTreeProvider: ContentTreeProvider
) {
final val logger = LoggerFactory.getLogger(ContentController::class.java)
) : Logging {
@RequestMapping(method = [RequestMethod.GET, RequestMethod.HEAD], value = ["/c/{id}"])
@ResponseBody
fun getResource(

View file

@ -1,6 +1,7 @@
package net.schowek.nextclouddlna.dlna.media
import net.schowek.nextclouddlna.nextcloud.content.ContentTreeProvider
import net.schowek.nextclouddlna.util.Logging
import org.jupnp.support.contentdirectory.AbstractContentDirectoryService
import org.jupnp.support.contentdirectory.ContentDirectoryErrorCode
import org.jupnp.support.contentdirectory.ContentDirectoryException
@ -12,7 +13,6 @@ import org.jupnp.support.model.DIDLContent
import org.jupnp.support.model.SortCriterion
import org.jupnp.support.model.container.Container
import org.jupnp.support.model.item.Item
import org.slf4j.LoggerFactory
import org.springframework.stereotype.Component
import java.util.*
import java.util.concurrent.TimeUnit.NANOSECONDS
@ -22,11 +22,10 @@ import java.util.concurrent.TimeUnit.NANOSECONDS
class ContentDirectoryService(
private val contentTreeProvider: ContentTreeProvider,
private val nodeConverter: NodeConverter
) :
AbstractContentDirectoryService(
mutableListOf("dc:title", "upnp:class"), // also "dc:creator", "dc:date", "res@size"
mutableListOf("dc:title")
) {
) : AbstractContentDirectoryService(
mutableListOf("dc:title", "upnp:class"), // also "dc:creator", "dc:date", "res@size"
mutableListOf("dc:title")
), Logging {
/**
* Root is requested with objectID="0".
@ -65,26 +64,20 @@ class ContentDirectoryService(
}
BrowseResult(DIDLParser().generate(DIDLContent()), 0, 0)
} catch (e: Exception) {
LOG.warn(
String.format(
"Failed to generate directory listing" +
" (objectID=%s, browseFlag=%s, filter=%s, firstResult=%s, maxResults=%s, orderby=%s).",
objectID, browseFlag, filter, firstResult, maxResults, Arrays.toString(orderby)
), e
logger.warn(
"Failed to generate directory listing (objectID={}, browseFlag={}, filter={}, firstResult={}, maxResults={}, orderby={}).",
objectID, browseFlag, filter, firstResult, maxResults, Arrays.toString(orderby), e
)
throw ContentDirectoryException(ContentDirectoryErrorCode.CANNOT_PROCESS, e.toString())
} finally {
LOG.info(
logger.info(
"Browse: {} ({}, {}) in {}ms.",
objectID, firstResult, maxResults,
NANOSECONDS.toMillis(System.nanoTime() - startTime)
objectID, firstResult, maxResults, NANOSECONDS.toMillis(System.nanoTime() - startTime)
)
}
}
companion object {
private val LOG = LoggerFactory.getLogger(ContentDirectoryService::class.java)
@Throws(Exception::class)
private fun toRangedResult(
containers: List<Container>,

View file

@ -1,10 +1,11 @@
package net.schowek.nextclouddlna.dlna.media
import jakarta.annotation.PostConstruct
import net.schowek.nextclouddlna.util.ExternalUrls
import net.schowek.nextclouddlna.util.Logging
import org.jupnp.model.meta.*
import org.jupnp.model.types.UDADeviceType
import org.jupnp.model.types.UDN.uniqueSystemIdentifier
import org.slf4j.LoggerFactory
import org.springframework.beans.factory.annotation.Qualifier
import org.springframework.beans.factory.annotation.Value
import org.springframework.stereotype.Component
@ -20,7 +21,7 @@ class MediaServer(
@Value("\${server.friendlyName}")
private val friendlyName: String,
private val externalUrls: ExternalUrls
) {
) : Logging {
val device = LocalDevice(
DeviceIdentity(uniqueSystemIdentifier("DLNAtoad-MediaServer"), 300),
UDADeviceType(DEVICE_TYPE, VERSION),
@ -28,21 +29,21 @@ class MediaServer(
createDeviceIcon(), arrayOf(contentDirectoryService, connectionManagerService)
)
init {
LOG.info("uniqueSystemIdentifier: {} ({})", device.identity.udn, friendlyName)
@PostConstruct
fun init() {
logger.info("uniqueSystemIdentifier: {} ({})", device.identity.udn, friendlyName)
}
companion object {
private const val DEVICE_TYPE = "MediaServer"
private const val VERSION = 1
private val LOG = LoggerFactory.getLogger(MediaServer::class.java)
const val ICON_FILENAME = "icon.png"
@Throws(IOException::class)
fun createDeviceIcon(): Icon {
val res = MediaServer::class.java.getResourceAsStream("/$ICON_FILENAME")
val resource = MediaServer::class.java.getResourceAsStream("/$ICON_FILENAME")
?: throw IllegalStateException("Icon not found.")
return res.use { res ->
return resource.use { res ->
Icon("image/png", 48, 48, 8, ICON_FILENAME, res).also {
it.validate()
}

View file

@ -1,5 +1,6 @@
package net.schowek.nextclouddlna.dlna.transport
import net.schowek.nextclouddlna.util.Logging
import org.apache.http.*
import org.apache.http.client.ResponseHandler
import org.apache.http.client.config.RequestConfig
@ -22,8 +23,6 @@ import org.jupnp.http.Headers
import org.jupnp.model.message.*
import org.jupnp.model.message.header.UpnpHeader
import org.jupnp.transport.spi.AbstractStreamClient
import org.jupnp.transport.spi.StreamClient
import org.slf4j.LoggerFactory
import java.nio.charset.Charset
import java.nio.charset.UnsupportedCharsetException
import java.util.concurrent.Callable
@ -31,7 +30,7 @@ import java.util.concurrent.Callable
class ApacheStreamClient(
private val configuration: ApacheStreamClientConfiguration
) : AbstractStreamClient<ApacheStreamClientConfiguration?, HttpRequestBase>() {
) : Logging, AbstractStreamClient<ApacheStreamClientConfiguration?, HttpRequestBase>() {
private val clientConnectionManager: PoolingHttpClientConnectionManager
private val httpClient: CloseableHttpClient
@ -143,8 +142,8 @@ class ApacheStreamClient(
request: HttpRequestBase
): Callable<StreamResponseMessage> {
return Callable<StreamResponseMessage> {
LOGGER.trace("Sending HTTP request: $requestMessage")
if (LOGGER.isTraceEnabled) {
logger.trace("Sending HTTP request: $requestMessage")
if (logger.isTraceEnabled) {
StreamsLoggerHelper.logStreamClientRequestMessage(requestMessage)
}
httpClient.execute<StreamResponseMessage>(request, createResponseHandler(requestMessage))
@ -159,26 +158,26 @@ class ApacheStreamClient(
if (t is IllegalStateException) {
// TODO: Document when/why this happens and why we can ignore it, violating the
// logging rules of the StreamClient#sendRequest() method
LOGGER.trace("Illegal state: " + t.message)
logger.trace("Illegal state: {}", t.message)
return true
} else if (t is NoHttpResponseException) {
LOGGER.trace("No Http Response: " + t.message)
logger.trace("No Http Response: {}", t.message)
return true
}
return false
}
override fun stop() {
LOGGER.trace("Shutting down HTTP client connection manager/pool")
logger.trace("Shutting down HTTP client connection manager/pool")
clientConnectionManager.shutdown()
}
private fun createHttpRequestEntity(upnpMessage: UpnpMessage<*>): HttpEntity {
return if (upnpMessage.bodyType == UpnpMessage.BodyType.BYTES) {
LOGGER.trace("Preparing HTTP request entity as byte[]")
logger.trace("Preparing HTTP request entity as byte[]")
ByteArrayEntity(upnpMessage.bodyBytes)
} else {
LOGGER.trace("Preparing HTTP request entity as string")
logger.trace("Preparing HTTP request entity as string")
var charset = upnpMessage.contentTypeCharset
if (charset == null) {
charset = "UTF-8"
@ -186,7 +185,7 @@ class ApacheStreamClient(
try {
StringEntity(upnpMessage.bodyString, charset)
} catch (ex: UnsupportedCharsetException) {
LOGGER.trace("HTTP request does not support charset: {}", charset)
logger.trace("HTTP request does not support charset: {}", charset)
throw RuntimeException(ex)
}
}
@ -195,7 +194,7 @@ class ApacheStreamClient(
private fun createResponseHandler(requestMessage: StreamRequestMessage?): ResponseHandler<StreamResponseMessage> {
return ResponseHandler<StreamResponseMessage> { httpResponse: HttpResponse ->
val statusLine = httpResponse.statusLine
LOGGER.trace("Received HTTP response: $statusLine")
logger.trace("Received HTTP response: $statusLine")
// Status
val responseOperation = UpnpResponse(statusLine.statusCode, statusLine.reasonPhrase)
@ -209,22 +208,22 @@ class ApacheStreamClient(
// Body
val entity = httpResponse.entity
if (entity == null || entity.contentLength == 0L) {
LOGGER.trace("HTTP response message has no entity")
logger.trace("HTTP response message has no entity")
return@ResponseHandler responseMessage
}
val data = EntityUtils.toByteArray(entity)
if (data != null) {
if (responseMessage.isContentTypeMissingOrText) {
LOGGER.trace("HTTP response message contains text entity")
logger.trace("HTTP response message contains text entity")
responseMessage.setBodyCharacters(data)
} else {
LOGGER.trace("HTTP response message contains binary entity")
logger.trace("HTTP response message contains binary entity")
responseMessage.setBody(UpnpMessage.BodyType.BYTES, data)
}
} else {
LOGGER.trace("HTTP response message has no entity")
logger.trace("HTTP response message has no entity")
}
if (LOGGER.isTraceEnabled) {
if (logger.isTraceEnabled) {
StreamsLoggerHelper.logStreamClientResponseMessage(responseMessage, requestMessage)
}
responseMessage
@ -232,7 +231,6 @@ class ApacheStreamClient(
}
companion object {
private val LOGGER = LoggerFactory.getLogger(StreamClient::class.java)
private fun addHeaders(httpMessage: HttpMessage, headers: Headers) {
for ((key, value1) in headers) {
for (value in value1) {

View file

@ -1,11 +1,11 @@
package net.schowek.nextclouddlna.dlna.transport
import com.sun.net.httpserver.HttpExchange
import net.schowek.nextclouddlna.util.Logging
import org.jupnp.model.message.*
import org.jupnp.protocol.ProtocolFactory
import org.jupnp.transport.spi.UpnpStream
import org.jupnp.util.io.IO
import org.slf4j.LoggerFactory
import java.io.IOException
import java.io.InputStream
import java.io.OutputStream
@ -15,7 +15,7 @@ import java.net.HttpURLConnection
abstract class MyHttpExchangeUpnpStream(
protocolFactory: ProtocolFactory?,
val httpExchange: HttpExchange
) : UpnpStream(protocolFactory) {
) : Logging, UpnpStream(protocolFactory) {
override fun run() {
try {
@ -116,9 +116,5 @@ abstract class MyHttpExchangeUpnpStream(
}
protected abstract fun createConnection(): Connection?
companion object {
private val logger = LoggerFactory.getLogger(MyHttpExchangeUpnpStream::class.java)
}
}

View file

@ -3,18 +3,19 @@ package net.schowek.nextclouddlna.dlna.transport
import com.sun.net.httpserver.HttpExchange
import com.sun.net.httpserver.HttpHandler
import com.sun.net.httpserver.HttpServer
import net.schowek.nextclouddlna.util.Logging
import org.jupnp.model.message.Connection
import org.jupnp.transport.Router
import org.jupnp.transport.spi.InitializationException
import org.jupnp.transport.spi.StreamServer
import org.slf4j.LoggerFactory
import java.io.IOException
import java.net.InetAddress
import java.net.InetSocketAddress
class MyStreamServerImpl(private val configuration: MyStreamServerConfiguration) :
StreamServer<MyStreamServerConfiguration> {
class MyStreamServerImpl(
private val configuration: MyStreamServerConfiguration
) : Logging, StreamServer<MyStreamServerConfiguration> {
protected var server: HttpServer? = null
@Synchronized
@ -95,9 +96,5 @@ class MyStreamServerImpl(private val configuration: MyStreamServerConfiguration)
return if (exchange.localAddress != null) exchange.localAddress.address else null
}
}
companion object {
private val logger = LoggerFactory.getLogger(MyStreamServerImpl::class.java)
}
}

View file

@ -1,14 +1,13 @@
package net.schowek.nextclouddlna.dlna.transport
import io.micrometer.common.util.StringUtils
import net.schowek.nextclouddlna.util.Logging
import org.jupnp.model.message.StreamRequestMessage
import org.jupnp.model.message.StreamResponseMessage
import org.jupnp.model.message.UpnpMessage
import org.slf4j.LoggerFactory
object StreamsLoggerHelper {
private val LOGGER = LoggerFactory.getLogger(StreamsLoggerHelper::class.java)
object StreamsLoggerHelper : Logging {
private const val HTTPSERVER_REQUEST_BEGIN =
"================================== HTTPSERVER REQUEST BEGIN ====================================="
private const val HTTPSERVER_REQUEST_END =
@ -30,7 +29,7 @@ object StreamsLoggerHelper {
val formattedRequest = getFormattedRequest(requestMessage)
val formattedHeaders = getFormattedHeaders(requestMessage)
val formattedBody = getFormattedBody(requestMessage)
LOGGER.trace(
logger.trace(
"Received a request from {}:\n{}\n{}{}{}{}",
requestMessage.connection.remoteAddress.hostAddress,
HTTPSERVER_REQUEST_BEGIN,
@ -45,7 +44,7 @@ object StreamsLoggerHelper {
val formattedResponse = getFormattedResponse(responseMessage)
val formattedHeaders = getFormattedHeaders(responseMessage)
val formattedBody = getFormattedBody(responseMessage)
LOGGER.trace(
logger.trace(
"Send a response to {}:\n{}\n{}{}{}{}",
requestMessage.connection.remoteAddress.hostAddress,
HTTPSERVER_RESPONSE_BEGIN,
@ -60,7 +59,7 @@ object StreamsLoggerHelper {
val formattedRequest = getFormattedRequest(requestMessage)
val formattedHeaders = getFormattedHeaders(requestMessage)
val formattedBody = getFormattedBody(requestMessage)
LOGGER.trace(
logger.trace(
"Send a request to {}:\n{}\n{}{}{}{}",
requestMessage.uri.host,
HTTPCLIENT_REQUEST_BEGIN,
@ -75,7 +74,7 @@ object StreamsLoggerHelper {
val formattedResponse = getFormattedResponse(responseMessage)
val formattedHeaders = getFormattedHeaders(responseMessage)
val formattedBody = getFormattedBody(responseMessage)
LOGGER.trace(
logger.trace(
"Received a response from {}:\n{}\n{}{}{}{}",
requestMessage?.uri?.host,
HTTPCLIENT_RESPONSE_BEGIN,
@ -109,7 +108,7 @@ object StreamsLoggerHelper {
}
}
}
if (headers.length > 0) {
if (headers.isNotEmpty()) {
headers.insert(0, "\nHEADER:\n")
}
return headers.toString()
@ -120,7 +119,8 @@ object StreamsLoggerHelper {
//message.isBodyNonEmptyString throw StringIndexOutOfBoundsException if string is empty
try {
val bodyNonEmpty = message.body != null &&
(message.body is String && (message.body as String).length > 0 || message.body is ByteArray && (message.body as ByteArray).size > 0)
(message.body is String && (message.body as String).isNotEmpty()
|| message.body is ByteArray && (message.body as ByteArray).isNotEmpty())
if (bodyNonEmpty && message.isBodyNonEmptyString) {
formattedBody = message.bodyString
}

View file

@ -1,57 +1,38 @@
package net.schowek.nextclouddlna.nextcloud
import jakarta.annotation.PostConstruct
import net.schowek.nextclouddlna.nextcloud.content.ContentItem
import net.schowek.nextclouddlna.nextcloud.content.ContentNode
import net.schowek.nextclouddlna.nextcloud.content.MediaFormat
import net.schowek.nextclouddlna.nextcloud.db.*
import net.schowek.nextclouddlna.nextcloud.db.Filecache.Companion.FOLDER_MIME_TYPE
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import net.schowek.nextclouddlna.util.Logging
import org.springframework.dao.InvalidDataAccessResourceUsageException
import org.springframework.stereotype.Component
import org.springframework.transaction.annotation.Transactional
import java.util.*
import java.util.function.Consumer
import java.util.function.Predicate
import kotlin.collections.HashMap
@Component
class MediaDB(
nextcloudConfig: NextcloudConfigDiscovery,
mimetypeRepository: MimetypeRepository,
filecacheRepository: FilecacheRepository,
groupFolderRepository: GroupFolderRepository
) {
final var logger: Logger = LoggerFactory.getLogger(MediaDB::class.java)
private val appdataDir: String
private val thumbStorageId: Int
private val contentDir: String
private val supportsGroupFolders: Boolean
private val mimetypeRepository: MimetypeRepository
private val filecacheRepository: FilecacheRepository
private val nextcloudConfig: NextcloudConfigDiscovery,
private val mimetypeRepository: MimetypeRepository,
private val filecacheRepository: FilecacheRepository,
private val groupFolderRepository: GroupFolderRepository
) : Logging {
private val thumbStorageId: Int = filecacheRepository.findFirstByPath(nextcloudConfig.appDataDir).storage
private val mimetypes: Map<Int, String> = mimetypeRepository.findAll().associate { it.id to it.mimetype }
private val folderMimeType: Int = mimetypes.entries.find { it.value == FOLDER_MIME_TYPE }!!.key
private val storageUsersMap: MutableMap<Int, String> = HashMap()
private val mimetypes: Map<Int, String>
private val folderMimeType: Int
init {
appdataDir = nextcloudConfig.appDataDir
contentDir = nextcloudConfig.nextcloudDir
supportsGroupFolders = nextcloudConfig.supportsGroupFolders
this.mimetypeRepository = mimetypeRepository
this.filecacheRepository = filecacheRepository
this.groupFolderRepository = groupFolderRepository
thumbStorageId = filecacheRepository.findFirstByPath(appdataDir).storage
@PostConstruct
fun init() {
logger.info("Using thumbnail storage id: {}", thumbStorageId)
mimetypes = mimetypeRepository.findAll().associate { it.id to it.mimetype }
folderMimeType = mimetypes.entries.find { it.value == FOLDER_MIME_TYPE }!!.key
}
@Transactional(readOnly = true)
fun processThumbnails(thumbConsumer: Consumer<ContentItem>) {
filecacheRepository.findThumbnails("$appdataDir/preview/%", thumbStorageId, folderMimeType).use { files ->
filecacheRepository.findThumbnails("${nextcloudConfig.appDataDir}/preview/%", thumbStorageId, folderMimeType).use { files ->
files.map { f: Filecache -> asItem(f) }.forEach(thumbConsumer)
}
}
@ -61,7 +42,7 @@ class MediaDB(
fun groupFolders(): List<ContentNode> {
when {
supportsGroupFolders -> {
nextcloudConfig.supportsGroupFolders -> {
try {
return groupFolderRepository.findAll().flatMap { g ->
filecacheRepository.findByPath("__groupfolders/" + g.id).map { f ->
@ -114,9 +95,9 @@ class MediaDB(
private fun buildPath(f: Filecache): String {
return if (storageUsersMap.containsKey(f.storage)) {
val userName: String? = storageUsersMap[f.storage]
contentDir + "/" + userName + "/" + f.path
nextcloudConfig.appDataDir + "/" + userName + "/" + f.path
} else {
contentDir + "/" + f.path
nextcloudConfig.appDataDir + "/" + f.path
}
}
}

View file

@ -1,7 +1,8 @@
package net.schowek.nextclouddlna.nextcloud
import jakarta.annotation.PostConstruct
import net.schowek.nextclouddlna.nextcloud.db.AppConfigRepository
import org.slf4j.LoggerFactory
import net.schowek.nextclouddlna.util.Logging
import org.springframework.beans.factory.annotation.Value
import org.springframework.stereotype.Component
import java.io.File
@ -15,15 +16,12 @@ class NextcloudConfigDiscovery(
@Value("\${nextcloud.filesDir}")
val nextcloudDir: String,
val appConfigRepository: AppConfigRepository
) {
final var logger = LoggerFactory.getLogger(NextcloudConfigDiscovery::class.java)
) : Logging {
final val appDataDir: String = findAppDataDir()
final val supportsGroupFolders: Boolean = checkGroupFoldersSupport()
final val appDataDir: String
final val supportsGroupFolders: Boolean
init {
appDataDir = findAppDataDir()
supportsGroupFolders = checkGroupFoldersSupport()
@PostConstruct
fun init() {
logger.info("Found appdata dir: {}", appDataDir)
}

View file

@ -2,7 +2,7 @@ package net.schowek.nextclouddlna.nextcloud.content
import jakarta.annotation.PostConstruct
import net.schowek.nextclouddlna.nextcloud.MediaDB
import org.slf4j.LoggerFactory
import net.schowek.nextclouddlna.util.Logging
import org.springframework.scheduling.annotation.Scheduled
import org.springframework.stereotype.Component
import java.util.concurrent.atomic.AtomicInteger
@ -12,10 +12,8 @@ import java.util.regex.Pattern
@Component
class ContentTreeProvider(
private val mediaDB: MediaDB
) {
final val logger = LoggerFactory.getLogger(ContentTreeProvider::class.java)
private var tree: ContentTree = buildContentTree()
) : Logging {
private var tree = buildContentTree()
private var lastMTime = 0L
init {
@ -24,7 +22,7 @@ class ContentTreeProvider(
@PostConstruct
@Scheduled(fixedDelay = 1000 * 60, initialDelay = 1000 * 60)
fun rebuildTree() {
final fun rebuildTree() {
val maxMtime: Long = mediaDB.maxMtime()
if (lastMTime < maxMtime) {
logger.info("ContentTree seems to be outdated - Loading...")

View file

@ -0,0 +1,14 @@
package net.schowek.nextclouddlna.util
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import kotlin.reflect.KClass
interface Logging {
val logger: Logger
get() = LoggerFactory.getLogger(classNameOf(this::class))
}
fun classNameOf(ownerClass: KClass<*>): String =
if (ownerClass.isCompanion) ownerClass.java.enclosingClass.name else ownerClass.java.name

View file

@ -1,7 +1,6 @@
package net.schowek.nextclouddlna.util
import jakarta.annotation.PostConstruct
import org.slf4j.LoggerFactory
import org.springframework.beans.factory.annotation.Value
import org.springframework.stereotype.Component
import java.net.*
@ -11,8 +10,7 @@ import java.util.*
class ServerInfoProvider(
@param:Value("\${server.port}") val port: Int,
@param:Value("\${server.interface}") private val networkInterface: String
) {
var logger = LoggerFactory.getLogger(ServerInfoProvider::class.java)
) : Logging {
var address: InetAddress? = null
@PostConstruct