Skip to content

Log

The log provider prints every call to your log, Logcat, Flutter's debug console, Xcode's console or Metro's output, instead of sending it anywhere. Use it in debug builds to see exactly what your app reports, and in what order: events, properties, sign-in, consent and start-up.

SDK Package
Android io.github.mkhytarmkhoian:herald-log
Flutter herald_log
iOS herald-ios, module HeraldLog
React Native @herald-analytics/log

Setup

It writes through AnalyticsLogger, so it depends on no logging library. On Android it's a one-method interface; on Flutter, iOS and React Native it's a function type, so debugPrint, { message in print(message) } or (message) => console.log(message) fits as-is. On iOS, Apple's Logger works too, and shows the lines in the Console app as well:

// Where the log lines go.
val logger = AnalyticsLogger { Log.d("analytics", it) }

// Handles events and user properties, using Herald's ready-made factories.
val logTracker = LogAnalyticsTrackerService(
    eventTrackerFactory = CompositeLogEventTrackerFactory(
        ScreenViewLogEventTrackerFactory(logger), // screen views
        GenericLogEventTrackerFactory(logger),    // every other event
    ),
    propertySetterFactory = CompositeLogPropertySetterFactory(
        GenericLogPropertySetterFactory(logger),
    ),
)
// Handles the rest: start-up, sign-in and sign-out, consent.
val logService = LogAnalyticsService(logger)

val herald = Herald {
    provider(
        name = "log",
        events = logTracker,
        properties = logTracker,
        identity = logService,
        lifecycle = logService,
        consent = logService,
    )
}
// Where the log lines go: print fits as-is.
let logger: AnalyticsLogger = { message in print(message) }

// Handles events and user properties, using Herald's ready-made factories.
let logTracker = LogAnalyticsTrackerService(
    eventTrackerFactory: CompositeLogEventTrackerFactory([
        ScreenViewLogEventTrackerFactory(logger: logger),  // screen views
        GenericLogEventTrackerFactory(logger: logger),  // every other event
    ]),
    propertySetterFactory: GenericLogPropertySetterFactory(logger: logger)
)
// Handles the rest: start-up, sign-in and sign-out, consent.
let logService = LogAnalyticsService(logger: logger)

let herald = Herald(providers: [
    HeraldProvider(
        name: "log",
        events: logTracker,
        properties: logTracker,
        identity: logService,
        lifecycle: logService,
        consent: logService
    )
])
// Where the log lines go: Flutter's debugPrint fits as-is.
final AnalyticsLogger logger = debugPrint;

// Handles events and user properties, using Herald's ready-made factories.
final logTracker = LogAnalyticsTrackerService(
  eventTrackerFactory: CompositeLogEventTrackerFactory([
    ScreenViewLogEventTrackerFactory(logger), // screen views
    GenericLogEventTrackerFactory(logger), // every other event
  ]),
  propertySetterFactory: CompositeLogPropertySetterFactory([
    GenericLogPropertySetterFactory(logger),
  ]),
);
// Handles the rest: start-up, sign-in and sign-out, consent.
final logService = LogAnalyticsService(logger);

final herald = Herald(
  providers: [
    HeraldProvider(
      name: 'log',
      events: logTracker,
      properties: logTracker,
      identity: logService,
      lifecycle: logService,
      consent: logService,
    ),
  ],
);
// Where the log lines go: the console fits as-is.
const logger: AnalyticsLogger = (message) => console.log(message);

// Handles events and user properties, using Herald's ready-made factories.
const logTracker = new LogAnalyticsTrackerService({
  eventTrackerFactory: new CompositeLogEventTrackerFactory([
    new ScreenViewLogEventTrackerFactory(logger), // screen views
    new GenericLogEventTrackerFactory(logger), // every other event
  ]),
  propertySetterFactory: new CompositeLogPropertySetterFactory([
    new GenericLogPropertySetterFactory(logger),
  ]),
});
// Handles the rest: start-up, sign-in and sign-out, consent.
const logService = new LogAnalyticsService(logger);

const herald = new Herald({
  providers: [
    new HeraldProvider({
      name: 'log',
      events: logTracker,
      properties: logTracker,
      identity: logService,
      lifecycle: logService,
      consent: logService,
    }),
  ],
});

Register it only in debug builds, next to your real vendors; on iOS, inside #if DEBUG, and on React Native when __DEV__ is true.

Output

Logcat
D/analytics: [herald] start
D/analytics: [herald] enabled true
D/analytics: [herald] user    demo-user
D/analytics: [herald] event   fare_selected
D/analytics:     ├─ fare     = 1 Day Pass
D/analytics:     ├─ price    = 5.0
D/analytics:     └─ ryder_id = Adult
D/analytics: [herald] prop    tickets_purchased = 2
D/analytics: [herald] reset

Flutter, iOS and React Native print the same lines, without Logcat's prefix. Parameters are sorted by name and aligned. A screen view is headed by its screen name.

Factories

The log provider has the same factories as a vendor module:

  • ScreenViewLogEventTrackerFactory;
  • GenericLogEventTrackerFactory and GenericLogPropertySetterFactory;
  • RequireMappedLogEventTrackerFactory and RequireMappedLogPropertySetterFactory.

End its chain with RequireMappedLogEventTrackerFactory, and a debug build reports every event that nobody mapped.

Debug builds only

The log prints the user id and every value as-is. That's the point in a debug build, and a leak in a release build.