Skip to content

Commit

Permalink
Log signals if debug mode is on (#1184)
Browse files Browse the repository at this point in the history
  • Loading branch information
silesky authored Nov 7, 2024
1 parent 2fc4c9c commit ccc97f1
Show file tree
Hide file tree
Showing 11 changed files with 166 additions and 66 deletions.
5 changes: 5 additions & 0 deletions .changeset/silly-papayas-buy.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@segment/analytics-signals': minor
---

Update logging
2 changes: 1 addition & 1 deletion packages/signals/signals-example/src/lib/analytics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ const isStage = process.env.STAGE === 'true'

const signalsPlugin = new SignalsPlugin({
...(isStage ? { apiHost: 'signals.segment.build/v1' } : {}),
enableDebugLogging: true,
// enableDebugLogging: true,
// processSignal: processSignalExample,
})

Expand Down
9 changes: 8 additions & 1 deletion packages/signals/signals/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,14 @@ You can *turn off debugging* by doing:
https://my-website.com?segment_signals_debug=false
```

* This also logs all signals to the js console.

#### Alternative method of enabling debug mode
In your JS console:
```js
SegmentSignalsPlugin.debug()
```

### Advanced

#### Listening to signals
Expand Down Expand Up @@ -108,4 +116,3 @@ Network signals emit when an HTTP Request is made, or an HTTP Response is receiv
- Initiated using the `fetch` API
- First party domain (e.g if on `foo.com`, then `foo.com/api/products`, but not `bar.com/api/products`)
- Contains the content-type: `application/json`

21 changes: 20 additions & 1 deletion packages/signals/signals/src/core/debug-mode/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,26 @@
export const parseDebugModeQueryString = (): boolean | undefined => {
const queryParams = new URLSearchParams(window.location.search)

const val = queryParams.get('segment_signals_debug')
const val =
queryParams.get('segment_signals_debug') ||
queryParams.get('seg_signals_debug')
if (val === 'true' || val === 'false') {
return val === 'true'
}
return undefined
}

/**
* This turns on advanced logging for signals!
*/
export const parseSignalsLoggingAdvancedQueryString = ():
| boolean
| undefined => {
const queryParams = new URLSearchParams(window.location.search)

const val =
queryParams.get('segment_signals_logging_advanced') ||
queryParams.get('seg_signals_logging_advanced')
if (val === 'true' || val === 'false') {
return val === 'true'
}
Expand Down
29 changes: 24 additions & 5 deletions packages/signals/signals/src/core/emitter/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,40 @@ export interface EmitSignal {
emit: (signal: Signal) => void
}

interface SignalEmitterSettings {
shouldLogSignals: () => boolean
}

export class SignalEmitter implements EmitSignal {
private emitter = new Emitter<{ add: [Signal] }>()

private listeners = new Set<(signal: Signal) => void>()
private settings?: SignalEmitterSettings
constructor(settings?: SignalEmitterSettings) {
this.settings = settings
}
emit(signal: Signal) {
logger.debug('new signal emitted', signal)
if (this.settings?.shouldLogSignals()) {
logger.log('New signal:', signal.type, signal.data)
}
this.emitter.emit('add', signal)
}

subscribe(broadcaster: (signal: Signal) => void) {
logger.debug('subscribed')
this.emitter.on('add', broadcaster)
subscribe(listener: (signal: Signal) => void) {
// Prevent duplicate subscriptions
if (!this.listeners.has(listener)) {
logger.debug('subscribed')
this.listeners.add(listener)
}
this.emitter.on('add', listener)
}

unsubscribe(listener: (signal: Signal) => void) {
this.listeners.delete(listener)
logger.debug('unsubscribed')
this.emitter.off('add', listener)
}

once(listener: (signal: Signal) => void) {
this.emitter.once('add', listener)
}
}
74 changes: 28 additions & 46 deletions packages/signals/signals/src/core/signals/settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { SignalsIngestSettingsConfig } from '../client'
import { SandboxSettingsConfig } from '../processor/sandbox'
import { NetworkSettingsConfig } from '../signal-generators/network-gen'
import { SignalsPluginSettingsConfig } from '../../types'
import { DebugStorage } from '../../lib/storage/debug-storage'

export type SignalsSettingsConfig = Pick<
SignalsPluginSettingsConfig,
Expand Down Expand Up @@ -33,9 +34,9 @@ export class SignalGlobalSettings {
signalBuffer: SignalBufferSettingsConfig
ingestClient: SignalsIngestSettingsConfig
network: NetworkSettingsConfig
signalsDebug: SignalsDebugSettings

private sampleSuccess = false
private signalsDebug = new SignalsDebugSettings()

constructor(settings: SignalsSettingsConfig) {
if (settings.maxBufferSize && settings.signalStorage) {
Expand Down Expand Up @@ -110,68 +111,49 @@ export class SignalGlobalSettings {
}
}

class SignalsDebugSettings {
export class SignalsDebugSettings {
private static redactionKey = 'segment_signals_debug_redaction_disabled'
private static ingestionKey = 'segment_signals_debug_ingestion_enabled'
private static logSignals = 'segment_signals_log_signals_enabled'
storage: DebugStorage

constructor(disableRedaction?: boolean, enableIngestion?: boolean) {
this.storage = new DebugStorage('sessionStorage')
if (typeof disableRedaction === 'boolean') {
this.setDebugKey(SignalsDebugSettings.redactionKey, disableRedaction)
this.storage.setDebugKey(
SignalsDebugSettings.redactionKey,
disableRedaction
)
}
if (typeof enableIngestion === 'boolean') {
this.setDebugKey(SignalsDebugSettings.ingestionKey, enableIngestion)
this.storage.setDebugKey(
SignalsDebugSettings.ingestionKey,
enableIngestion
)
}

// setting ?segment_signals_debug=true will disable redaction, enable ingestion, and set keys in local storage
// this setting will persist across page loads (even if there is no query string)
// in order to clear the setting, user must set ?segment_signals_debug=false
const debugModeInQs = parseDebugModeQueryString()
logger.debug('debugMode is set to true via query string')
if (typeof debugModeInQs === 'boolean') {
this.setDebugKey(SignalsDebugSettings.redactionKey, debugModeInQs)
this.setDebugKey(SignalsDebugSettings.ingestionKey, debugModeInQs)
this.setAllDebugging(debugModeInQs)
}
}

setDebugKey(key: string, enable: boolean) {
try {
if (enable) {
window.sessionStorage.setItem(key, 'true')
} else {
logger.debug(`Removing debug key ${key} from storage`)
window.sessionStorage.removeItem(key)
}
} catch (e) {
logger.debug('Storage error', e)
}
setAllDebugging = (boolean: boolean) => {
this.storage.setDebugKey(SignalsDebugSettings.redactionKey, boolean)
this.storage.setDebugKey(SignalsDebugSettings.ingestionKey, boolean)
this.storage.setDebugKey(SignalsDebugSettings.logSignals, boolean)
}

getDisableSignalsRedaction() {
try {
const isEnabled = Boolean(
window.sessionStorage.getItem(SignalsDebugSettings.redactionKey)
)
if (isEnabled) {
logger.debug(`${SignalsDebugSettings.redactionKey}=true (app. storage)`)
return true
}
} catch (e) {
logger.debug('Storage error', e)
}
return false
getDisableSignalsRedaction = (): boolean => {
return this.storage.getDebugKey(SignalsDebugSettings.redactionKey)
}

getEnableSignalsIngestion() {
try {
const isEnabled = Boolean(
window.sessionStorage.getItem(SignalsDebugSettings.ingestionKey)
)
if (isEnabled) {
logger.debug(`${SignalsDebugSettings.ingestionKey}=true (app. storage)`)
return true
}
} catch (e) {
logger.debug('Storage error', e)
}
return false
getEnableSignalsIngestion = (): boolean => {
return this.storage.getDebugKey(SignalsDebugSettings.ingestionKey)
}

getEnableLogSignals = (): boolean => {
return this.storage.getDebugKey(SignalsDebugSettings.logSignals)
}
}
14 changes: 12 additions & 2 deletions packages/signals/signals/src/core/signals/signals.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,10 @@ export class Signals implements ISignals {
private globalSettings: SignalGlobalSettings
constructor(settingsConfig: SignalsSettingsConfig = {}) {
this.globalSettings = new SignalGlobalSettings(settingsConfig)
this.signalEmitter = new SignalEmitter()
this.signalEmitter = new SignalEmitter({
shouldLogSignals: () =>
this.globalSettings.signalsDebug.getEnableLogSignals(),
})
this.signalsClient = new SignalsIngestClient(
this.globalSettings.ingestClient
)
Expand Down Expand Up @@ -130,7 +133,14 @@ export class Signals implements ISignals {
}

/**
* Emit custom signals.
* Disable redaction, ingestion of signals, and other debug logging.
*/
debug(boolean = true): void {
this.globalSettings.signalsDebug.setAllDebugging(boolean)
}

/**
* Register custom signal generators to emit signals.
*/
async registerGenerator(
generators: (SignalGeneratorClass | SignalGenerator)[]
Expand Down
30 changes: 23 additions & 7 deletions packages/signals/signals/src/lib/logger/index.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,31 @@
import { parseSignalsLoggingAdvancedQueryString } from '../../core/debug-mode'
import { DebugStorage } from '../storage/debug-storage'

class Logger {
globalKey = 'SEGMENT_SIGNALS_DEBUG'
get debugLoggingEnabled() {
return (window as any)[this.globalKey] === true
private static advancedLogging = 'segment_signals_logging_advanced'

storage = new DebugStorage('sessionStorage')
constructor() {
const val = parseSignalsLoggingAdvancedQueryString()
if (typeof val === 'boolean') {
this.storage.setDebugKey(Logger.advancedLogging, val)
}
}

private debugLoggingEnabled = (): boolean => {
return this.storage.getDebugKey(Logger.advancedLogging)
}

enableDebugLogging = (bool = true) => {
this.storage.setDebugKey(Logger.advancedLogging, bool)
}

enableDebugLogging() {
;(window as any)[this.globalKey] = true
log = (...args: any[]): void => {
console.log('[signals log]', ...args)
}

debug(...args: any[]): void {
if (this.debugLoggingEnabled) {
debug = (...args: any[]): void => {
if (this.debugLoggingEnabled()) {
console.log('[signals debug]', ...args)
}
}
Expand Down
29 changes: 29 additions & 0 deletions packages/signals/signals/src/lib/storage/debug-storage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
export class DebugStorage {
private storageType: 'localStorage' | 'sessionStorage'
constructor(type: 'localStorage' | 'sessionStorage') {
this.storageType = type
}
public setDebugKey = (key: string, enable: boolean): void => {
try {
if (enable) {
window[this.storageType].setItem(key, 'true')
} else {
window.sessionStorage.removeItem(key)
}
} catch (e) {
console.warn('Storage error', e)
}
}

public getDebugKey = (key: string): boolean => {
try {
const isEnabled = Boolean(window[this.storageType].getItem(key))
if (isEnabled) {
return true
}
} catch (e) {
console.warn('Storage error', e)
}
return false
}
}
17 changes: 15 additions & 2 deletions packages/signals/signals/src/plugin/signals-plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,19 @@ export class SignalsPlugin implements Plugin, SignalsAugmentedFunctionality {
readonly name = 'SignalsPlugin'
readonly version = version
public signals: Signals

constructor(settings: SignalsPluginSettingsConfig = {}) {
assertBrowserEnv()

// assign to window for debugging purposes
Object.assign(window, { SegmentSignalsPlugin: this })

if (settings.enableDebugLogging) {
logger.enableDebugLogging()
}
logger.debug('SignalsPlugin initializing', { settings })

logger.debug(`SignalsPlugin v${version} initializing`, {
settings,
})

this.signals = new Signals({
disableSignalsRedaction: settings.disableSignalsRedaction,
Expand Down Expand Up @@ -79,4 +85,11 @@ export class SignalsPlugin implements Plugin, SignalsAugmentedFunctionality {
this.signals.signalEmitter.emit(signal)
return this
}

/**
* Enable redaction and disable ingestion of signals. Also, logs signals to the console.
*/
debug(boolean = true): void {
this.signals.debug(boolean)
}
}
2 changes: 1 addition & 1 deletion packages/signals/signals/src/types/process-signal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export interface AnalyticsRuntimePublicApi {

export type ProcessSignalScope = {
analytics: AnalyticsRuntimePublicApi
signals: SignalsRuntime
signals: SignalsRuntime<Signal>
} & typeof WebRuntimeConstants

export interface ProcessSignal {
Expand Down

0 comments on commit ccc97f1

Please sign in to comment.