-
Notifications
You must be signed in to change notification settings - Fork 129
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
RUMM-2171 Use core feature scope in Logger #881
Conversation
) | ||
} | ||
|
||
internal func createSDKUserLogger( | ||
configuration: InternalLoggerConfiguration, | ||
in core: DatadogCoreProtocol, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The userLogger
now needs a core
instance with a registered LoggingFeature
instance :/
An alternative would be to provide 2 different implementations of a Logger, one that uses core
, and one that write logs directly to the console without core
. LMKWYT!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Makes sense to have separate implementation for core logger. After all, we will need something self-contained that will live solely in Core module, so we can't use Logger
nor even LogOutput
in there. I think that a thin CoreLogger
construct can make sense here, with not much "outputs" abstraction and a simple print()
at at the bottom 👌.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, a question is: should the core logger be specific to an instance of the core
or can we have a single core logger for all SDK instances? I think the prior makes more sense, as messages could be annotated with the SDK instance name. Otherwise, internal logs could be ambiguous.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed, we should get a console print per core
👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It looks great at the high level 🎯👌. I agree that we have a problem with internal loggers now. I'd vote for solving it by doing more refactoring in this PR instead of adding bool flags in Logger.init()
. I left some details in comments, LTMWDYT 🙌.
let storage: FeatureStorage | ||
let telemetry: Telemetry? | ||
|
||
func execute(_ block: (DatadogV1Context, Writer) throws -> Void) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This piece is called "event write context" in the original RFC to highlight the purpose of this method: obtaining the context for writing events in V2 (getting async SDK context + writer). Here we call it execute
which is quite vague given that this method cannot (and shouldn't) be used for anything else. What's your take on this @maxep?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
execute
by itself doesn't mean much I agree, but as the single method of a protocol called FeatureScope
it describes what it does: core.scope(...).execute { }
-> it execute the block in the feature's scope
We could use something more explicit like executeEventWriteContext
but it looks long and redundant from a usage perspective, no?
The get
prefix, as proposed in the RFC, should be reserved to method that return something IMO.
this method cannot (and shouldn't) be used for anything else
Shouldn't we enforce this by design rather than convention? By defining a method that expect an event that is then written to the storage:
func buildEvent<T>(_ block: (DatadogV1Context) throws -> T?) where T: Encodable
the returned event would be then passed to the writer.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see - indeed it makes more sense if thinking of "scope" + "execute" rather than "execute" itself 👍. I'm only worried that we won't stay in sync with Android SDK. One of the goals in the RFC was to embrace a common language, hence we introduced the concept of "event write context" that is a central point to V2 storage architecture. I'm OK with changing it, but we should try to preserve common lingo between both SDKs, as it will make discussion and plannings easier.
func buildEvent<T>(_ block: (DatadogV1Context) throws -> T?) where T: Encodable
Mind that soon we will enhance the "event write context" with metadata concept and the writer
interface will become more specialised:
// ref. RFC:
internal struct BatchWriter<Event: Codable, Metadata: Codable> {
let currentMetadata: Metadata
func write(event: Event, newMetadata: Metadata) { /* ... */ }
}
so I don't think that buildEvent<T>
will be able to support this. I think that starting with convention might be good for the beginning - we can change it later when we understand more. WDYT @maxep?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Indeed, I forgot about the metadata :) I will rename the method to eventWriteContext
to stay aligned with Android for now, we can continue discussing naming as we move forward with the transition.
internal let logOutput: LogOutput? | ||
/// Provides date for log creation. | ||
private let dateProvider: DateProvider | ||
internal typealias LogEventValidation = (LogEvent) -> Bool |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks very odd without additional context and I think it's only because of internal SDK-logger (?). If so, I'd vote on refactoring userLogger
as part of this PR. In V2 we won't be able to use real Logger
to print internal debug messages, so such change will be very much aligned with V2 migration. Also, we don't really leverage Logger
capabilities in userLogger
, so replacing it with simpler construct should be quite easy and not much impactful. Last, it will simplify this PR a bit. WDYT @maxep ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm preparing for rebasing on #867 which allow filtering logs levels in dd logger as well, so this closure will still be necessary.
I'm planning on re-writing the console log, but I would tackle that in a dedicated PR so we can discuss its design. How does it sounds?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sounds good 👍. We can consolidate this validation later, after we see the whole picture 👌.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've added a JIRA ticket: RUMM-2239
private let dateProvider: DateProvider | ||
internal typealias LogEventValidation = (LogEvent) -> Bool | ||
|
||
internal let core: DatadogCoreProtocol |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't have yet opinion on this, but should we capture strong reference to core
in Features @maxep ?
By making it weak
we can anticipate capability to tear down the SDK instance making its Features disabled. On the other hand, if choosing weak
we might need to provide additional thread-safety measures for accessing core
from Feature's thread (so it can be deallocated at any time in atomic fashion). Maybe it's better to start with strong reference.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's a very good question! IMO, the core should exist as long as the feature exists, so strong
keeps a consistent behavior.
Also, the DatadogCoreProtocol
is not a class-only protocol by inheriting from AnyObject
. Maybe that is something we should add, so Features can decide if core should be kept!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's keep it strong
now and we will see when migrating other features if there's a use case for weak
. There is no plan yet for "SDK can be deinitialized", so strong
reference can be considered simpler solution for now 👍.
) | ||
} | ||
|
||
internal func createSDKUserLogger( | ||
configuration: InternalLoggerConfiguration, | ||
in core: DatadogCoreProtocol, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Makes sense to have separate implementation for core logger. After all, we will need something self-contained that will live solely in Core module, so we can't use Logger
nor even LogOutput
in there. I think that a thin CoreLogger
construct can make sense here, with not much "outputs" abstraction and a simple print()
at at the bottom 👌.
Co-authored-by: Maciek Grzybowski <[email protected]>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All good 🏅
What and why?
Logger
instances now build and record events usingV1FeatureScope
provided byDatadogV1CoreProtocol
.How?
The
Logger
now keeps a reference to the providedcore
to retrieve the scope when a log event needs to be recorded. TheLogEventBuilder
is created using the context provided with the scope, the event is then written to thewriter
provided by core, and an additional log output if available. The additional output allow to write the event to the console and/or to RUM Error Integration.The
userLogger
static instance is now instantiated using thecore
created atDatadog.initialize()
.Review checklist
Custom CI job configuration (optional)