Logging
public struct Logging
This type provides access to the App’s loggers.
Logging in Flint is slightly different and solves several problems:
- Excessively noisy logs in non-development builds (a separate logger for debug logging, always silenced in production)
- The inability to tell what actual user activity a log entry relates to (see: topic paths, contextual logging)
- The typical reliance on a specific logging framework. Wire up whatever implementation you like here.
- Focusing on logs only related to specific application features, e.g. drill down into just your
Share
feature’s logging
Abstracting logging is one of the more laughable and tedious things in computing, after all how many ways do we need to log text? However nothing out there supports contextual logging and topic paths which are crucial for Flint. So here we are.
Flint’s logging is somewhat decoupled from the rest of Flint and works like this:
- Something in the App calls
Logging.development?.contextualLogger(...)
orLogging.production.contextualLogger(...)
to get a logger that has information about what the user is doing. - The resulting
ContextSpecificLogger
, if not nil is passed to subsystems that require logging. This is the biggest difference with other logging systems that assume the logger never changes. - The subsystems call one of the logging functions on the logger.
- The
ContextualLogger
delegates the actual logging to aContextualLoggerTarget
, which can filter events or log levels as desired - The
DefaultContextualLoggerTarget
filters events according to Focus rules, and sends output to aLoggerOutput
instance which is the final output of the log text.
The chain of execution is along these lines:
ContextualLogger
-> ContextualLoggerTarget
-> LoggerOutput
The LoggerOutput
is the only think you need to implement for your logging framework, e.g. a layer that uses CocoaLumberjack or Apple’s log systems,
or Fabric’s CLS_LOG
rolling log buffer.
Production logging is always available, so that logger factory is not optional. Debug logging can be entirely disabled for production builds, resulting is close to zero overheads due to the optional dereferencing.
This solves the problem of polluting production logs with excessive internal debug info, without having to dial down log levels in production.
The implementation of ContextualLoggerTarget
can peform filtering of events by topic path or other properties of the
context
(see DefaultLogger
) and those ContextSpecificLogger
implementations then pass the logging info on to the
LoggerOutput
implementation which writes to whatever output you desire.
There is an AggregatingLoggerOutput
provided so you can compose multiple log outputs easily and drive them from
the same contextual loggers, e.g. to output to Console as well as an ASL log file.
See
DefaultLoggerFactory.setup()
for the simple console logging used by default when using Flint.quickSetup
.
-
Undocumented
Declaration
Swift
public static var development: ContextualLoggerFactory?
-
Code using this logger always causes some overhead, as there must be a production logger set. Loggers should always test the log level first before evaluation the log text, so that @autoclosure can be used to avoid evaluation of the input data if the log level is not appropriate.
Declaration
Swift
public static var production: ContextualLoggerFactory!
-
Called internally to set up the outputs for the factories
Declaration
Swift
public static func setLoggerOutputs(development developmentOutputs: [LoggerOutput]?, developmentLevel: LoggerLevel, production productionOutputs: [LoggerOutput]?, productionLevel: LoggerLevel)