Skip to content

Commit

Permalink
feat: basic logging on iOS (#14)
Browse files Browse the repository at this point in the history
* Add TemplateContext and related classes

* Add TemplateContext and associated classes to LogContext

* Add MessageParser and DateTimeFormat tests

* Add IgnoreIos annotation to skip tests running on iOS (iOS is not implemented yet and would fail)

* Finish adding tests

* Add KTags and add it to KLogContext

* Add Platform and DefaultPlatform

* Formatting

* Fix iOS build error

* Fix findLoggingClass not working on Kotlin through StackWalker

* Formatting

* Remove print

* Fix tests

* Implement basic logging capabilities with NSLog

* Add capability to log with OSLog on iOS

* Apply formatting

* Add functioing KFluentLogger

* Update NSThreadBasedCallerFinder

* Implement basic logging

* Formatting

* Enable autoservice again

* Fix jvm tests

* update kflogger publish

* Add printf formatting

* fmt

* add improvements

* fix tests

* add lazy evaluations

* fmt

* fmt:
  • Loading branch information
buenaflor committed Sep 18, 2023
1 parent ac0e436 commit e724eeb
Show file tree
Hide file tree
Showing 311 changed files with 2,824 additions and 1,479 deletions.
94 changes: 77 additions & 17 deletions api/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import java.util.*
import org.jetbrains.kotlin.gradle.internal.KaptWithoutKotlincTask
import org.jetbrains.kotlin.gradle.plugin.KotlinDependencyHandler

Expand All @@ -6,6 +7,7 @@ plugins {
alias(libs.plugins.errorprone)
kotlin("kapt")
`maven-publish`
signing
}

group = properties["groupName"].toString()
Expand Down Expand Up @@ -142,42 +144,100 @@ private val jvmFluentLoggerTest by
tasks.registering(Test::class) {
filter {
includeTestsMatching("*.testCreate")
includeTestsMatching("KPlatformTest.testNotCrashing")
includeTestsMatching("*.testNotCrashing")
includeTestsMatching("*.testFindLoggingClass")
}
dependsOn(deleteServiceFilesFromBuildDir)
}

tasks.named<Test>("jvmTest") {
filter {
excludeTestsMatching("*.testCreate")
excludeTestsMatching("KPlatformTest.testNotCrashing")
excludeTestsMatching("*.testNotCrashing")
excludeTestsMatching("*.testFindLoggingClass")
}
finalizedBy(jvmFluentLoggerTest)
}

fun KotlinDependencyHandler.errorprone(dependencyNotation: Any): Dependency? {
val dependency = implementation(dependencyNotation)
val dependency = compileOnly(dependencyNotation)
return dependency?.let {
project.configurations.getByName("errorprone").dependencies.add(dependency)
it
}
}

publishing {
// TODO: Create a separate publication for "system-backend"
publications {
// Define the publication with the desired artifact name
create<MavenPublication>("KFlogger") {
artifactId = "kflogger"
groupId = group.toString()
version = version.toString()

from(components["kotlin"])
}
}
// Stub secrets to let the project sync and build without the publication values set up
ext["signing.keyId"] = null

ext["signing.password"] = null

ext["signing.secretKeyRingFile"] = null

ext["ossrhUsername"] = null

ext["ossrhPassword"] = null

// Grabbing secrets from local.properties file or from environment variables, which could be used on
// CI
val secretPropsFile = project.rootProject.file("local.properties")

if (secretPropsFile.exists()) {
secretPropsFile
.reader()
.use { Properties().apply { load(it) } }
.onEach { (name, value) -> ext[name.toString()] = value }
} else {
ext["signing.keyId"] = System.getenv("SIGNING_KEY_ID")
ext["signing.password"] = System.getenv("SIGNING_PASSWORD")
ext["signing.secretKeyRingFile"] = System.getenv("SIGNING_SECRET_KEY_RING_FILE")
ext["ossrhUsername"] = System.getenv("OSSRH_USERNAME")
ext["ossrhPassword"] = System.getenv("OSSRH_PASSWORD")
}

val javadocJar by tasks.registering(Jar::class) { archiveClassifier.set("javadoc") }

fun getExtraString(name: String) = ext[name]?.toString()

publishing {
repositories {
// Configure the Maven repository where we want to publish
mavenLocal()
maven {
name = "sonatype"
setUrl("https://s01.oss.sonatype.org/content/repositories/releases/")
credentials {
username = getExtraString("ossrhUsername")
password = getExtraString("ossrhPassword")
}
}
publications.withType<MavenPublication> {
artifactId = artifactId.replace("api", "kflogger")

// Stub javadoc.jar artifact
// artifact(javadocJar.get())

// Provide artifacts information requited by Maven Central
pom {
name.set("KFlogger")
description.set("Kotlin Multiplatform port of Flogger")
url.set("https://github.com/buenaflor/kflogger")

licenses {
license {
name.set("Apache License 2.0")
url.set("https://opensource.org/license/apache-2-0/")
}
}
developers {
developer {
id.set("buenaflor")
name.set("Giancarlo Buenaflor")
email.set("giancarlo_buenaflor@yahoo.com")
}
}
scm { url.set("https://github.com/buenaflor/KFlogger") }
}
}
}
}

signing { sign(publishing.publications) }
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.buenaflor.kflogger
package com.giancarlobuenaflor.kflogger

import com.buenaflor.kflogger.backend.KLogData
import com.buenaflor.kflogger.backend.KLoggerBackend
import com.giancarlobuenaflor.kflogger.backend.KLogData
import com.giancarlobuenaflor.kflogger.backend.KLoggerBackend

/**
* Base class for the fluent logger API. This class is a factory for instances of a fluent logging
Expand All @@ -28,7 +28,7 @@ protected constructor(backend: KLoggerBackend) {
* where `NO_OP` is a singleton, no-op instance of the logging API whose methods do nothing and
* just `return noOp()`.
*/
public abstract fun at(level: KLevel?): API
public abstract fun at(level: KLevel): API

/** A convenience method for at([Level.SEVERE]). */
public fun atSevere(): API
Expand Down Expand Up @@ -62,7 +62,7 @@ protected constructor(backend: KLoggerBackend) {
* Returns whether the given level is enabled for this logger. Users wishing to guard code with a
* check for "loggability" should use `logger.atLevel().isEnabled()` instead.
*/
public fun isLoggable(level: KLevel?): Boolean
public fun isLoggable(level: KLevel): Boolean

// ---- IMPLEMENTATION DETAIL (only visible to the base logging context) ----
/**
Expand All @@ -75,9 +75,3 @@ protected constructor(backend: KLoggerBackend) {
*/
public fun write(data: KLogData)
}

/**
* Returns the logging backend (not visible to logger subclasses to discourage tightly coupled
* implementations).
*/
public expect val <API : KLoggingApi<API>> KAbstractLogger<API>.backend: KLoggerBackend
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
package com.buenaflor.kflogger
package com.giancarlobuenaflor.kflogger

import com.giancarlobuenaflor.kflogger.backend.KLoggerBackend

// This is a workaround for nested classes/interfaces not being accessible through typealiases
// See: https://youtrack.jetbrains.com/issue/KT-34281
Expand All @@ -16,8 +18,9 @@ public interface KFluentLoggerApi : KLoggingApi<KFluentLoggerApi>
* The choice to prevent direct extension of loggers was made deliberately to ensure that users of a
* specific logger implementation always get the same behavior.
*/
public expect class KFluentLogger : KAbstractLogger<KFluentLoggerApi> {
override fun at(level: KLevel?): KFluentLoggerApi
public expect class KFluentLogger internal constructor(backend: KLoggerBackend) :
KAbstractLogger<KFluentLoggerApi> {
override fun at(level: KLevel): KFluentLoggerApi

public companion object {
/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.giancarlobuenaflor.kflogger

/**
* Functional interface for allowing lazily evaluated arguments to be supplied to Flogger. This
* allows callers to defer argument evaluation efficiently when:
* <ul>
* <li>Doing "fine" logging that's normally disabled
* <li>Applying rate limiting to log statements
* </ul>
*/
public expect fun interface KLazyArg<T> {
/**
* Computes a value to use as a log argument. This method is invoked once the Flogger library has
* determined that logging will occur, and the returned value is used in place of the `LazyArg`
* instance that was passed into the log statement.
*/
public fun evaluate(): T?
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package com.giancarlobuenaflor.kflogger

/**
* Static utility methods for lazy argument evaluation in Flogger. The [.lazy] method allows lambda
* expressions to be "cast" to the [LazyArg] interface.
*
* In cases where the log statement is strongly expected to always be enabled (e.g. unconditional
* logging at warning or above) it may not be worth using lazy evaluation because any work required
* to evaluate arguments will happen anyway.
*
* If lambdas are available, users should prefer using this class rather than explicitly creating
* `LazyArg` instances.
*/
// TODO: Add other generally useful methods here, especially things which help non-lambda users.
public expect class KLazyArgs {
public companion object {
/**
* Coerces a lambda expression or method reference to return a lazily evaluated logging
* argument. Pass in a compatible, no-argument, lambda expression or method reference to have it
* evaluated only when logging will actually occur.
* <pre>`logger.atFine().log("value=%s", lazy(() -> doExpensive()));
* logger.atWarning().atMostEvery(5, MINUTES).log("value=%s", lazy(stats::create));
* `</pre> *
*
* Evaluation of lazy arguments occurs at most once, and always in the same thread from which
* the logging call was made.
*
* Note also that it is almost never suitable to make a `toString()` call "lazy" using this
* mechanism and, in general, explicitly calling `toString()` on arguments which are being
* logged is an error as it precludes the ability to log an argument structurally.
*/
public fun <T> lazy(lambdaOrMethodReference: KLazyArg<T>): KLazyArg<T>
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.buenaflor.kflogger
package com.giancarlobuenaflor.kflogger

public expect class KLevel {
public companion object {
Expand Down Expand Up @@ -70,3 +70,5 @@ public expect class KLevel {
public val ALL: KLevel
}
}

public expect fun KLevel.intValue(): Int
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
package com.buenaflor.kflogger
package com.giancarlobuenaflor.kflogger

import com.buenaflor.kflogger.backend.KLogData
import com.buenaflor.kflogger.backend.KMetadata
import com.buenaflor.kflogger.backend.KTemplateContext
import com.buenaflor.kflogger.context.KTags
import com.buenaflor.kflogger.parser.KMessageParser
import com.giancarlobuenaflor.kflogger.backend.KLogData
import com.giancarlobuenaflor.kflogger.backend.KMetadata
import com.giancarlobuenaflor.kflogger.backend.KTemplateContext
import com.giancarlobuenaflor.kflogger.context.KTags
import com.giancarlobuenaflor.kflogger.parser.KMessageParser

/**
* The base context for a logging statement, which implements the base logging API.
Expand Down Expand Up @@ -83,7 +83,7 @@ public expect abstract class KLogContext<LOGGER : KAbstractLogger<API>, API : KL

public final override fun getLogSite(): KLogSite

public final override fun getTemplateContext(): KTemplateContext
public final override fun getTemplateContext(): KTemplateContext?

public final override fun getArguments(): Array<Any?>?

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.buenaflor.kflogger
package com.giancarlobuenaflor.kflogger

/**
* Provides a strategy for "bucketing" a potentially unbounded set of log aggregation keys used by
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
package com.buenaflor.kflogger
package com.giancarlobuenaflor.kflogger

public expect class KLogRecord(level: KLevel, message: String)
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.buenaflor.kflogger
package com.giancarlobuenaflor.kflogger

/**
* A value type which representing the location of a single log statement. This class is similar to
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
* limitations under the License.
*/

package com.buenaflor.kflogger
package com.giancarlobuenaflor.kflogger

/**
* A tagging interface to mark implementations that are suitable for use as a key for looking up per
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.giancarlobuenaflor.kflogger

public expect class KLogger {
public fun isLoggable(level: KLevel): Boolean
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.buenaflor.kflogger
package com.giancarlobuenaflor.kflogger

/**
* The basic logging API. An implementation of this API (or an extension of it) will be returned by
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.buenaflor.kflogger
package com.giancarlobuenaflor.kflogger

/**
* An opaque scope marker which can be attached to log sites to provide "per scope" behaviour for
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.buenaflor.kflogger
package com.giancarlobuenaflor.kflogger

/**
* Provides a scope to a log statement via the [LogContext.per] method.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.buenaflor.kflogger
package com.giancarlobuenaflor.kflogger

/**
* Callback interface to handle additional contextual `Metadata` in log statements. This interface
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package com.buenaflor.kflogger
package com.giancarlobuenaflor.kflogger

import com.buenaflor.kflogger.backend.KMetadata
import com.giancarlobuenaflor.kflogger.backend.KMetadata

/**
* Status for rate limiting operations, usable by rate limiters and available to subclasses of
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.buenaflor.kflogger
package com.giancarlobuenaflor.kflogger

/**
* Enum values to be passed into [GoogleLoggingApi.withStackTrace] to control the maximum number of
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.buenaflor.kflogger
package com.giancarlobuenaflor.kflogger

public expect enum class KTimeUnit {
/** Time unit representing one thousandth of a microsecond. */
Expand Down
16 changes: 16 additions & 0 deletions api/src/commonMain/kotlin/com.giancarlobuenaflor.kflogger/Klass.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.giancarlobuenaflor.kflogger

import kotlin.reflect.KClass

/**
* The bridge class used to typealias `Class` for JVM and `KClass` for iOS. However, Class and
* KClass have different signatures, so it is not possible to automatically typealias them with the
* same class at once. The solution here is to save the `KClass` as a property of `Klass` in the iOS
* implementation.
*
* Accessing the respective `Class` or `KClass` is done via the `toKlass()` extension function.
*/
public expect class Klass<T : Any>

/** Extension function to convert a `KClass` to a `Klass`. */
public expect fun <T : Any> KClass<T>.toKlass(): Klass<T>
Loading

0 comments on commit e724eeb

Please sign in to comment.