Skip to content

feat(logging): add public log listener methods #1580

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

Open
wants to merge 4 commits into
base: main
Choose a base branch
from

Conversation

nan-li
Copy link
Contributor

@nan-li nan-li commented Jul 1, 2025

Description

One Line Summary

Adds the following new public methods to allow an app developer to capture logs as strings at runtime.

Details

This is a copy of changes in #1576

Adds the following new public methods:

  • OneSignal.Debug.addLogListener
  • OneSignal.Debug.removeLogListener

These new methods provide a way to capture all SDK log entires at runtime, allowing the app developer to store these and/or send them to their server.

The log listener is independent of logLevel, all message are always sent to listeners.

Sample Usage

// AppDelegate.h
// Add OSLogListener after UIApplicationDelegate
@interface AppDelegate : UIResponder <UIApplicationDelegate, OSLogListener>
@end

// AppDelegate.m
@implementation AppDelegate
- (BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
  // Add your AppDelegate as an observer
  [OneSignal.Debug addLogListener:self];
}

// Add this new method
- (void)onLogEvent:(OneSignalLogEvent * _Nonnull)event {
  NSLog(@"onLogEvent: %lu - %@", (unsigned long)event.level, event.entry);
}
@end

// Remove the observer
[OneSignal.Debug removeLogListener:self];
// AppDelegate.swift
// Add OSLogListener after UIApplicationDelegate
class AppDelegate: UIResponder, UIApplicationDelegate, OSLogListener {
  func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
    // Add your AppDelegate as an observer
    OneSignal.Debug.addLogListener(self)
  }

  // Add this new method
  func onLogEvent(_ event: OneSignalLogEvent) {
    print("onLogEvent: \(event.level) - \(event.entry)")
  }
}
     
// Remove the observer
OneSignal.Debug.removeLogListener(self)

Motivation

App developers want a way to capture all SDK logs and send them to their server to debug issues in production.

Scope

Only add new methods to OneSignal.Debug to allow of capturing logs at runtime.
Create new OSCopyOnWriteSet class

Testing

Unit testing

Added LoggingTests

Manual testing

Tested on an iPhone 13, tested concurrent modification of log listeners array as well (within the same thread and from multiple threads)

Affected code checklist

  • Notifications
    • Display
    • Open
    • Push Processing
    • Confirm Deliveries
  • Outcomes
  • Sessions
  • In-App Messaging
  • REST API requests
  • Public API changes

Checklist

Overview

  • I have filled out all REQUIRED sections above
  • PR does one thing
  • Any Public API changes are explained in the PR details and conform to existing APIs

Testing

  • I have included test coverage for these changes, or explained why they are not needed
  • All automated tests pass, or I explained why that is not possible
  • I have personally tested this on my device, or explained why that is not possible

Final pass

  • Code is as readable as possible.
  • I have reviewed this PR myself, ensuring it meets each checklist item

This change is Reviewable

nan-li added 4 commits July 1, 2025 11:07
* Adds the following new public methods:
   - OneSignal.Debug.addLogListener
   - OneSignal.Debug.removeLogListener
* These new methods provide a way to capture all SDK log entires at runtime, allowing the app developer to store these and/or send them to their server.
* The log listener is independent of logLevel, all message are always sent to listeners.
* Use the new `OSCopyOnWriteSet`: it is possible for someone to add or remove a log listener within the `onLogEvent` callback, thus modifying the set of listeners as the set is being enumerated. @synchronized is a recursive lock, so it would not be sufficient. Locking the entire set of listeners would result in deadlock.
* This mimics the Android implementation which uses the built-in `CopyOnWriteArraySet`
@nan-li nan-li requested review from jkasten2 and jinliu9508 July 1, 2025 18:15
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.

2 participants