Skip to content

feat: Telemetry API #863

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Jun 14, 2023
Merged

feat: Telemetry API #863

merged 6 commits into from
Jun 14, 2023

Conversation

aajtodd
Copy link
Contributor

@aajtodd aajtodd commented Jun 13, 2023

Issue #

N/A

Description of changes

Proposes a new unified interface for telemetry data. This interface aligns much closer to OpenTelemetry such that plugging in OTeL as the backing implementation should be easy (we may make tweaks along the way if we discover this not to be the case).

NOTE: This is just the interface, removal of previous tracing/logging and updating usage across the SDK will be in subsequent PRs.

By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.

@aajtodd aajtodd requested a review from a team as a code owner June 13, 2023 12:39
Comment on lines 33 to 36
/**
* Get the [LoggerProvider] used to create new [aws.smithy.kotlin.runtime.telemetry.logging.LoggerProvider] instances
*/
public val loggerProvider: LoggerProvider
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: used to create new aws.smithy.kotlin.runtime.telemetry.logging.Logger instances

Comment on lines +13 to +34
/**
* Construct a logging record and emits it to an underlying logger
*/
public interface LogRecordBuilder {
/**
* Set the logging level (severity)
* @param level the level to log this record at
*/
public fun setLevel(level: LogLevel)

/**
* Set the timestamp
* @param ts the observed time for this event
*/
public fun setTimestamp(ts: Instant)

/**
* Set an exception associated with this event
* Some loggers will do additional formatting for exceptions.
* @param ex the exception to associate with this log record
*/
public fun setCause(ex: Throwable)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Question: Values that can be written but not read feel unergonomic to me, as do explicit setters (although I understand that Kotlin does not yet support write-only properties). Is there a reason not to use full read/write vars here?

interface LogRecordBuilder {
    var level: LogLevel
    var timestamp: Instant
    var cause: Throwable
    ...
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the idea is to be no-ops in many cases (e.g. not being sampled, log level not enabled, etc).

Comment on lines +56 to +65
/**
* Associate all key/value pairs from [attributes] with this log record
* @param attributes the attributes to associate with this log record
*/
public fun setAllAttributes(attributes: Attributes) {
attributes.keys.forEach { key ->
@Suppress("UNCHECKED_CAST")
setAttribute(key.name, attributes[key as AttributeKey<Any>])
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Style: The name of this method seems misleading to me since it does not replace all the attributes (which is what "set" typically means). Maybe it "adds" or "merges" them?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe just setAttributes then?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

setAttributes also implies to me that all existing attributes would be replaced. In my preference, either we want that and the default impl changes here or we want to merge attributes and the method name changes to reflect that.

Comment on lines 37 to 40
/**
* Lazy add a log message with throwable payload if trace logging is enabled
*/
public fun trace(t: Throwable?, msg: () -> Any)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Question: Given that t is nullable here, is there a reason to offer an explicit overload without the Throwable argument? Could we have just one overload with a default value?

fun trace(t: Throwable? = null, msg: () -> Any)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We probably could yes. I think there may be some changes here still anyway FWIW since I need to figure out how we want to think about trace context and logs.

Comment on lines 22 to 27
/**
* Get or create a named [Meter]
* @param scope the name of the instrumentation scope that uniquely identifies this meter
* @param attributes instrumentation scope attributes to associate with emitted telemetry
*/
public fun getMeter(scope: String, attributes: Attributes = emptyAttributes()): Meter
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Style: The word "get" by itself typically implies a read-only operation but that's not what's described in the method docs. Kotlin's stdlib calls this sort of thing getOrPut but I've also seen getOrCreate.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can change it (and tracer provider) to getOrCreate if you prefer.

Comment on lines 35 to 39
/**
* Set all [attributes] on the span.
* @param attributes collection of attributes to set on the span
*/
public fun setAttributes(attributes: Attributes)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Question: Does this mean "replace all existing attributes with the new attributes value"? Or merge?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Merge

Comment on lines 28 to 33
/**
* Set an attribute on the span
* @param key the attribute key to use
* @param value the value to associate with the key
*/
public fun <T : Any> setAttribute(key: AttributeKey<T>, value: T)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Style: Consider indexed getter/setter:

operator fun <T : Any> get(key: AttributeKey<T>): T?
operator fun <T : Any> set(key: AttributeKey<T>, value: T)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You'll find most of the API is going to be write only and that is on purpose but we could change it to operator functions.

Comment on lines +48 to +52
/**
* Set the span status
* @param status the status to set
*/
public fun setStatus(status: SpanStatus)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment: As above, write but no read feels weird.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These are all write only in OTeL API (the SDK implementation allows reading attributes and other values in processors and exporters where they make sampling decisions, serialize, etc). If we made them readable we'd have to store the value in the shim layer(s) even though they may not be used. We don't really need to read any of this anywhere though.

Comment on lines 54 to 58
/**
* Marks the end of this span's execution. This MUST be called when the unit
* of work the span represents has finished.
*/
public fun end()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Question: If this MUST be called, should TraceSpan be Closeable and should end instead be close?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we wanted to go that route yes it could be.

Comment on lines 22 to 31
/**
* Returns a unique [Tracer] scoped to be used by instrumentation code. The scope
* and identity of that instrumentation code is uniquely identified by the name
* and attributes.
*
* @param scope the name of the instrumentation scope
* @param attributes (optional) specifies the instrumentation scope attributes to associate with emitted
* telemetry
*/
public fun getTracer(scope: String, attributes: Attributes = emptyAttributes()): Tracer
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Question: Is this also "get or create"?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes.

@sonarqubecloud
Copy link

Kudos, SonarCloud Quality Gate passed!    Quality Gate passed

Bug A 0 Bugs
Vulnerability A 0 Vulnerabilities
Security Hotspot A 0 Security Hotspots
Code Smell A 33 Code Smells

No Coverage information No Coverage information
0.0% 0.0% Duplication

import aws.smithy.kotlin.runtime.util.get

/**
* Construct a logging record and emits it to an underlying logger
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This builder doesn't emit to an underlying logger, it just constructs a logging record, right?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct, will fix in next PR.

@aajtodd aajtodd merged commit 1c281fa into observability Jun 14, 2023
@aajtodd aajtodd deleted the telemetry-api branch June 14, 2023 19:14
aajtodd added a commit that referenced this pull request Jul 7, 2023
aajtodd added a commit that referenced this pull request Jul 17, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants