@kubesense/kubesense-browser-logs 0.0.0-stage → 2.0.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (166) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +90 -2
  3. package/bundle/kubesense-logs.js +1 -0
  4. package/cjs/boot/logsPublicApi.d.ts +231 -0
  5. package/cjs/boot/logsPublicApi.js +75 -0
  6. package/cjs/boot/logsPublicApi.js.map +1 -0
  7. package/cjs/boot/preStartLogs.d.ts +8 -0
  8. package/cjs/boot/preStartLogs.js +105 -0
  9. package/cjs/boot/preStartLogs.js.map +1 -0
  10. package/cjs/boot/startLogs.d.ts +35 -0
  11. package/cjs/boot/startLogs.js +54 -0
  12. package/cjs/boot/startLogs.js.map +1 -0
  13. package/cjs/domain/assembly.d.ts +5 -0
  14. package/cjs/domain/assembly.js +35 -0
  15. package/cjs/domain/assembly.js.map +1 -0
  16. package/cjs/domain/configuration.d.ts +306 -0
  17. package/cjs/domain/configuration.js +86 -0
  18. package/cjs/domain/configuration.js.map +1 -0
  19. package/cjs/domain/console/consoleCollection.d.ts +20 -0
  20. package/cjs/domain/console/consoleCollection.js +37 -0
  21. package/cjs/domain/console/consoleCollection.js.map +1 -0
  22. package/cjs/domain/contexts/commonContext.d.ts +2 -0
  23. package/cjs/domain/contexts/commonContext.js +13 -0
  24. package/cjs/domain/contexts/commonContext.js.map +1 -0
  25. package/cjs/domain/contexts/internalContext.d.ts +7 -0
  26. package/cjs/domain/contexts/internalContext.js +11 -0
  27. package/cjs/domain/contexts/internalContext.js.map +1 -0
  28. package/cjs/domain/contexts/rumInternalContext.d.ts +2 -0
  29. package/cjs/domain/contexts/rumInternalContext.js +32 -0
  30. package/cjs/domain/contexts/rumInternalContext.js.map +1 -0
  31. package/cjs/domain/contexts/sessionContext.d.ts +4 -0
  32. package/cjs/domain/contexts/sessionContext.js +20 -0
  33. package/cjs/domain/contexts/sessionContext.js.map +1 -0
  34. package/cjs/domain/contexts/trackingConsentContext.d.ts +3 -0
  35. package/cjs/domain/contexts/trackingConsentContext.js +14 -0
  36. package/cjs/domain/contexts/trackingConsentContext.js.map +1 -0
  37. package/cjs/domain/createErrorFieldFromRawError.d.ts +10 -0
  38. package/cjs/domain/createErrorFieldFromRawError.js +15 -0
  39. package/cjs/domain/createErrorFieldFromRawError.js.map +1 -0
  40. package/cjs/domain/hooks.d.ts +28 -0
  41. package/cjs/domain/hooks.js +13 -0
  42. package/cjs/domain/hooks.js.map +1 -0
  43. package/cjs/domain/lifeCycle.d.ts +24 -0
  44. package/cjs/domain/lifeCycle.js +6 -0
  45. package/cjs/domain/lifeCycle.js.map +1 -0
  46. package/cjs/domain/logger/isAuthorized.d.ts +17 -0
  47. package/cjs/domain/logger/isAuthorized.js +34 -0
  48. package/cjs/domain/logger/isAuthorized.js.map +1 -0
  49. package/cjs/domain/logger/loggerCollection.d.ts +7 -0
  50. package/cjs/domain/logger/loggerCollection.js +47 -0
  51. package/cjs/domain/logger/loggerCollection.js.map +1 -0
  52. package/cjs/domain/logger.d.ts +48 -0
  53. package/cjs/domain/logger.js +112 -0
  54. package/cjs/domain/logger.js.map +1 -0
  55. package/cjs/domain/networkError/networkErrorCollection.d.ts +9 -0
  56. package/cjs/domain/networkError/networkErrorCollection.js +57 -0
  57. package/cjs/domain/networkError/networkErrorCollection.js.map +1 -0
  58. package/cjs/domain/plugins.d.ts +49 -0
  59. package/cjs/domain/plugins.js +12 -0
  60. package/cjs/domain/plugins.js.map +1 -0
  61. package/cjs/domain/remoteSdkConfiguration.d.ts +26 -0
  62. package/cjs/domain/remoteSdkConfiguration.js +67 -0
  63. package/cjs/domain/remoteSdkConfiguration.js.map +1 -0
  64. package/cjs/domain/report/reportCollection.d.ts +13 -0
  65. package/cjs/domain/report/reportCollection.js +30 -0
  66. package/cjs/domain/report/reportCollection.js.map +1 -0
  67. package/cjs/domain/reportError.d.ts +2 -0
  68. package/cjs/domain/reportError.js +20 -0
  69. package/cjs/domain/reportError.js.map +1 -0
  70. package/cjs/domain/runtimeError/runtimeErrorCollection.d.ts +14 -0
  71. package/cjs/domain/runtimeError/runtimeErrorCollection.js +29 -0
  72. package/cjs/domain/runtimeError/runtimeErrorCollection.js.map +1 -0
  73. package/cjs/domainContext.types.d.ts +11 -0
  74. package/cjs/entries/main.d.ts +33 -0
  75. package/cjs/entries/main.js +31 -0
  76. package/cjs/entries/main.js.map +1 -0
  77. package/cjs/logsEvent.types.d.ts +192 -0
  78. package/cjs/rawLogsEvent.types.d.ts +59 -0
  79. package/cjs/transport/startLogsBatch.d.ts +4 -0
  80. package/cjs/transport/startLogsBatch.js +22 -0
  81. package/cjs/transport/startLogsBatch.js.map +1 -0
  82. package/cjs/transport/startLogsBridge.d.ts +2 -0
  83. package/cjs/transport/startLogsBridge.js +13 -0
  84. package/cjs/transport/startLogsBridge.js.map +1 -0
  85. package/esm/boot/logsPublicApi.mjs +75 -0
  86. package/esm/boot/logsPublicApi.mjs.map +1 -0
  87. package/esm/boot/preStartLogs.mjs +105 -0
  88. package/esm/boot/preStartLogs.mjs.map +1 -0
  89. package/esm/boot/startLogs.mjs +54 -0
  90. package/esm/boot/startLogs.mjs.map +1 -0
  91. package/esm/domain/assembly.mjs +35 -0
  92. package/esm/domain/assembly.mjs.map +1 -0
  93. package/esm/domain/configuration.mjs +84 -0
  94. package/esm/domain/configuration.mjs.map +1 -0
  95. package/esm/domain/console/consoleCollection.mjs +36 -0
  96. package/esm/domain/console/consoleCollection.mjs.map +1 -0
  97. package/esm/domain/contexts/commonContext.mjs +13 -0
  98. package/esm/domain/contexts/commonContext.mjs.map +1 -0
  99. package/esm/domain/contexts/internalContext.mjs +11 -0
  100. package/esm/domain/contexts/internalContext.mjs.map +1 -0
  101. package/esm/domain/contexts/rumInternalContext.mjs +32 -0
  102. package/esm/domain/contexts/rumInternalContext.mjs.map +1 -0
  103. package/esm/domain/contexts/sessionContext.mjs +20 -0
  104. package/esm/domain/contexts/sessionContext.mjs.map +1 -0
  105. package/esm/domain/contexts/trackingConsentContext.mjs +14 -0
  106. package/esm/domain/contexts/trackingConsentContext.mjs.map +1 -0
  107. package/esm/domain/createErrorFieldFromRawError.mjs +15 -0
  108. package/esm/domain/createErrorFieldFromRawError.mjs.map +1 -0
  109. package/esm/domain/hooks.mjs +13 -0
  110. package/esm/domain/hooks.mjs.map +1 -0
  111. package/esm/domain/lifeCycle.mjs +7 -0
  112. package/esm/domain/lifeCycle.mjs.map +1 -0
  113. package/esm/domain/logger/isAuthorized.mjs +32 -0
  114. package/esm/domain/logger/isAuthorized.mjs.map +1 -0
  115. package/esm/domain/logger/loggerCollection.mjs +47 -0
  116. package/esm/domain/logger/loggerCollection.mjs.map +1 -0
  117. package/esm/domain/logger.mjs +110 -0
  118. package/esm/domain/logger.mjs.map +1 -0
  119. package/esm/domain/networkError/networkErrorCollection.mjs +56 -0
  120. package/esm/domain/networkError/networkErrorCollection.mjs.map +1 -0
  121. package/esm/domain/plugins.mjs +12 -0
  122. package/esm/domain/plugins.mjs.map +1 -0
  123. package/esm/domain/remoteSdkConfiguration.mjs +65 -0
  124. package/esm/domain/remoteSdkConfiguration.mjs.map +1 -0
  125. package/esm/domain/report/reportCollection.mjs +30 -0
  126. package/esm/domain/report/reportCollection.mjs.map +1 -0
  127. package/esm/domain/reportError.mjs +20 -0
  128. package/esm/domain/reportError.mjs.map +1 -0
  129. package/esm/domain/runtimeError/runtimeErrorCollection.mjs +29 -0
  130. package/esm/domain/runtimeError/runtimeErrorCollection.mjs.map +1 -0
  131. package/esm/entries/main.mjs +27 -0
  132. package/esm/entries/main.mjs.map +1 -0
  133. package/esm/transport/startLogsBatch.mjs +22 -0
  134. package/esm/transport/startLogsBatch.mjs.map +1 -0
  135. package/esm/transport/startLogsBridge.mjs +13 -0
  136. package/esm/transport/startLogsBridge.mjs.map +1 -0
  137. package/package.json +53 -3
  138. package/src/boot/logsPublicApi.ts +427 -0
  139. package/src/boot/preStartLogs.ts +192 -0
  140. package/src/boot/startLogs.ts +83 -0
  141. package/src/domain/assembly.ts +73 -0
  142. package/src/domain/configuration.ts +176 -0
  143. package/src/domain/console/consoleCollection.ts +58 -0
  144. package/src/domain/contexts/commonContext.ts +15 -0
  145. package/src/domain/contexts/internalContext.ts +19 -0
  146. package/src/domain/contexts/rumInternalContext.ts +60 -0
  147. package/src/domain/contexts/sessionContext.ts +34 -0
  148. package/src/domain/contexts/trackingConsentContext.ts +18 -0
  149. package/src/domain/createErrorFieldFromRawError.ts +22 -0
  150. package/src/domain/hooks.ts +40 -0
  151. package/src/domain/lifeCycle.ts +25 -0
  152. package/src/domain/logger/isAuthorized.ts +33 -0
  153. package/src/domain/logger/loggerCollection.ts +69 -0
  154. package/src/domain/logger.ts +202 -0
  155. package/src/domain/networkError/networkErrorCollection.ts +104 -0
  156. package/src/domain/plugins.ts +72 -0
  157. package/src/domain/remoteSdkConfiguration.ts +105 -0
  158. package/src/domain/report/reportCollection.ts +47 -0
  159. package/src/domain/reportError.ts +20 -0
  160. package/src/domain/runtimeError/runtimeErrorCollection.ts +51 -0
  161. package/src/domainContext.types.ts +21 -0
  162. package/src/entries/main.ts +65 -0
  163. package/src/logsEvent.types.ts +198 -0
  164. package/src/rawLogsEvent.types.ts +74 -0
  165. package/src/transport/startLogsBatch.ts +29 -0
  166. package/src/transport/startLogsBridge.ts +13 -0
@@ -0,0 +1,83 @@
1
+ import type { BufferedObservable, BufferedData, SessionManager } from '@kubesense/kubesense-browser-core'
2
+ import {
3
+ sendToExtension,
4
+ canUseEventBridge,
5
+ startAccountContext,
6
+ startGlobalContext,
7
+ startUserContext,
8
+ startTabContext,
9
+ } from '@kubesense/kubesense-browser-core'
10
+ import type { LogsConfiguration } from '../domain/configuration'
11
+ import { startLogsAssembly } from '../domain/assembly'
12
+ import { startConsoleCollection } from '../domain/console/consoleCollection'
13
+ import { startReportCollection } from '../domain/report/reportCollection'
14
+ import { startNetworkErrorCollection } from '../domain/networkError/networkErrorCollection'
15
+ import { startRuntimeErrorCollection } from '../domain/runtimeError/runtimeErrorCollection'
16
+ import { LifeCycle, LifeCycleEventType } from '../domain/lifeCycle'
17
+ import { startLoggerCollection } from '../domain/logger/loggerCollection'
18
+ import { startLogsBatch } from '../transport/startLogsBatch'
19
+ import { startLogsBridge } from '../transport/startLogsBridge'
20
+ import { startInternalContext } from '../domain/contexts/internalContext'
21
+ import { startReportError } from '../domain/reportError'
22
+ import type { CommonContext } from '../rawLogsEvent.types'
23
+ import type { Hooks } from '../domain/hooks'
24
+ import { startRUMInternalContext } from '../domain/contexts/rumInternalContext'
25
+ import { startSessionContext } from '../domain/contexts/sessionContext'
26
+
27
+ const LOGS_STORAGE_KEY = 'logs'
28
+
29
+ export type StartLogs = typeof startLogs
30
+ export type StartLogsResult = ReturnType<StartLogs>
31
+
32
+ export function startLogs(
33
+ configuration: LogsConfiguration,
34
+ sessionManager: SessionManager,
35
+ getCommonContext: () => CommonContext,
36
+ bufferedDataObservable: BufferedObservable<BufferedData>,
37
+ hooks: Hooks
38
+ ) {
39
+ const lifeCycle = new LifeCycle()
40
+ const cleanupTasks: Array<() => void> = []
41
+
42
+ lifeCycle.subscribe(LifeCycleEventType.LOG_COLLECTED, (log) => sendToExtension('logs', log))
43
+
44
+ const reportError = startReportError(lifeCycle)
45
+
46
+ // Start user and account context first to allow overrides from global context
47
+ const { assembleEventDefaults: assembleHook } = hooks
48
+ startSessionContext(assembleHook, configuration, sessionManager)
49
+ const accountContext = startAccountContext(assembleHook, configuration, LOGS_STORAGE_KEY)
50
+ const userContext = startUserContext(assembleHook, configuration, sessionManager, LOGS_STORAGE_KEY)
51
+ const globalContext = startGlobalContext(assembleHook, configuration, LOGS_STORAGE_KEY, false)
52
+ startRUMInternalContext(hooks)
53
+ startTabContext(assembleHook)
54
+
55
+ startNetworkErrorCollection(configuration, lifeCycle, bufferedDataObservable)
56
+ startRuntimeErrorCollection(configuration, lifeCycle, bufferedDataObservable)
57
+ startConsoleCollection(configuration, lifeCycle, bufferedDataObservable)
58
+ bufferedDataObservable.unbuffer()
59
+ startReportCollection(configuration, lifeCycle)
60
+ const { handleLog } = startLoggerCollection(lifeCycle)
61
+
62
+ startLogsAssembly(configuration, lifeCycle, hooks, getCommonContext, reportError)
63
+
64
+ if (!canUseEventBridge()) {
65
+ const { stop: stopLogsBatch } = startLogsBatch(configuration, lifeCycle, reportError, sessionManager)
66
+ cleanupTasks.push(() => stopLogsBatch())
67
+ } else {
68
+ startLogsBridge(lifeCycle)
69
+ }
70
+
71
+ const internalContext = startInternalContext(sessionManager)
72
+
73
+ return {
74
+ handleLog,
75
+ getInternalContext: internalContext.get,
76
+ accountContext,
77
+ globalContext,
78
+ userContext,
79
+ stop: () => {
80
+ cleanupTasks.forEach((task) => task())
81
+ },
82
+ }
83
+ }
@@ -0,0 +1,73 @@
1
+ import type { Context, EventRateLimiter } from '@kubesense/kubesense-browser-core'
2
+ import { ErrorSource, buildTags, createEventRateLimiter } from '@kubesense/kubesense-browser-core'
3
+ import { toRelativeTime } from '@kubesense/kubesense-js-core/time'
4
+ import { DISCARDED } from '@kubesense/kubesense-js-core/assembly'
5
+ import { combine } from '@kubesense/kubesense-js-core/util'
6
+ import type { CommonContext } from '../rawLogsEvent.types'
7
+ import type { LogsEvent } from '../logsEvent.types'
8
+ import type { LogsConfiguration } from './configuration'
9
+ import type { LifeCycle } from './lifeCycle'
10
+ import { LifeCycleEventType } from './lifeCycle'
11
+ import { STATUSES } from './logger'
12
+ import type { Hooks, AssembleHookParams } from './hooks'
13
+
14
+ export function startLogsAssembly(
15
+ configuration: LogsConfiguration,
16
+ lifeCycle: LifeCycle,
17
+ hooks: Hooks,
18
+ getCommonContext: () => CommonContext,
19
+ reportError: (message: string) => void,
20
+ eventRateLimit?: number
21
+ ) {
22
+ const statusWithCustom = (STATUSES as string[]).concat(['custom'])
23
+ const logRateLimiters: { [key: string]: EventRateLimiter } = {}
24
+ statusWithCustom.forEach((status) => {
25
+ logRateLimiters[status] = createEventRateLimiter(status, reportError, eventRateLimit)
26
+ })
27
+
28
+ lifeCycle.subscribe(
29
+ LifeCycleEventType.RAW_LOG_COLLECTED,
30
+ ({ rawLogsEvent, savedCommonContext = undefined, domainContext, ktags = [] }) => {
31
+ const startTime = toRelativeTime(rawLogsEvent.date)
32
+ const commonContext = savedCommonContext || getCommonContext()
33
+ const assemblyParams: AssembleHookParams = {
34
+ startTime,
35
+ rawLogsEvent: rawLogsEvent as AssembleHookParams['rawLogsEvent'],
36
+ domainContext,
37
+ }
38
+ const defaultLogsEventAttributes = hooks.assembleEventDefaults.trigger(assemblyParams)
39
+ if (defaultLogsEventAttributes === DISCARDED) {
40
+ return
41
+ }
42
+
43
+ const logsEventAttributes = hooks.assembleEvent.trigger(assemblyParams)!
44
+ if (logsEventAttributes === DISCARDED) {
45
+ return
46
+ }
47
+
48
+ const defaultKtags = buildTags(configuration)
49
+
50
+ const log = combine(
51
+ {
52
+ view: commonContext.view,
53
+ },
54
+ defaultLogsEventAttributes,
55
+ rawLogsEvent,
56
+ logsEventAttributes,
57
+ {
58
+ ktags: defaultKtags.concat(ktags).join(','),
59
+ }
60
+ ) as LogsEvent & Context
61
+
62
+ if (
63
+ configuration.beforeSend?.(log, domainContext) === false ||
64
+ (log.origin !== ErrorSource.AGENT &&
65
+ (logRateLimiters[log.status] ?? logRateLimiters['custom']).isLimitReached())
66
+ ) {
67
+ return
68
+ }
69
+
70
+ lifeCycle.notify(LifeCycleEventType.LOG_COLLECTED, log)
71
+ }
72
+ )
73
+ }
@@ -0,0 +1,176 @@
1
+ import type { InitConfiguration, RawTelemetryConfiguration } from '@kubesense/kubesense-browser-core'
2
+ import { ConsoleApiName } from '@kubesense/kubesense-js-core/util'
3
+ import {
4
+ serializeConfiguration,
5
+ catchUserErrors,
6
+ display,
7
+ RawReportType,
8
+ BROWSER_CORE_SCHEMA,
9
+ } from '@kubesense/kubesense-browser-core'
10
+ import { validateAndBuildConfiguration } from '@kubesense/kubesense-js-core/configuration'
11
+ import type { InferredConfig } from '@kubesense/kubesense-js-core/configuration'
12
+ import type { LogsEvent } from '../logsEvent.types'
13
+ import type { LogsEventDomainContext } from '../domainContext.types'
14
+ import type { LogsPlugin } from './plugins'
15
+
16
+ /**
17
+ * Init Configuration for the Logs browser SDK.
18
+ *
19
+ * @category Main
20
+ * @example NPM
21
+ * ```ts
22
+ * import { kubesenseLogs } from '@kubesense/kubesense-browser-logs'
23
+ *
24
+ * kubesenseLogs.init({
25
+ * clientToken: '<KUBESENSE_CLIENT_TOKEN>',
26
+ * kubesenseRumEndpoint: '<KUBESENSE_RUM_ENDPOINT>',
27
+ * // ...
28
+ * })
29
+ * ```
30
+ * @example CDN
31
+ * ```ts
32
+ * KUBESENSE_LOGS.init({
33
+ * clientToken: '<KUBESENSE_CLIENT_TOKEN>',
34
+ * kubesenseRumEndpoint: '<KUBESENSE_RUM_ENDPOINT>',
35
+ * // ...
36
+ * })
37
+ * ```
38
+ */
39
+ export interface LogsInitConfiguration extends InitConfiguration {
40
+ /**
41
+ * Access to every logs collected by the Logs SDK before they are sent to KubeSense.
42
+ * It allows:
43
+ * - Enrich your logs with additional context attributes
44
+ * - Modify your logs to modify their content, or redact sensitive sequences (see the list of editable properties)
45
+ * - Discard selected logs
46
+ *
47
+ * @category Data Collection
48
+ */
49
+ beforeSend?: LogsBeforeSend | undefined
50
+
51
+ /**
52
+ * Forward uncaught exceptions and network errors to KubeSense.
53
+ *
54
+ * To capture `console.error` calls, use {@link forwardConsoleLogs} with `"error"` (or `"all"`).
55
+ *
56
+ * @category Data Collection
57
+ * @defaultValue true
58
+ * @see forwardConsoleLogs
59
+ */
60
+ forwardErrorsToLogs?: boolean | undefined
61
+
62
+ /**
63
+ * Forward logs from console.* to KubeSense. Use "all" to forward everything or an array of console API names to forward only a subset.
64
+ *
65
+ * @category Data Collection
66
+ */
67
+ forwardConsoleLogs?: ConsoleApiName[] | 'all' | undefined
68
+
69
+ /**
70
+ * Forward reports from the [Reporting API](https://developer.mozilla.org/en-US/docs/Web/API/Reporting_API) to KubeSense. Use "all" to forward everything or an array of report types to forward only a subset.
71
+ *
72
+ * @category Data Collection
73
+ */
74
+ forwardReports?: RawReportType[] | 'all' | undefined
75
+
76
+ /**
77
+ * List of plugins to enable. The plugins API is unstable and experimental, and may change without
78
+ * notice. Please use only plugins provided by KubeSense matching the version of the SDK you are
79
+ * using.
80
+ *
81
+ * @experimental
82
+ */
83
+ plugins?: LogsPlugin[] | undefined
84
+
85
+ /**
86
+ * Enable dashboard-managed SDK settings served by the KubeSense collector.
87
+ *
88
+ * Logs reads the same cached document as RUM, but only consumes the `features.logs` switch and
89
+ * the `logs` section. The document is fetched asynchronously after the SDK starts, so it never
90
+ * delays initialization, and cached settings are applied on the next page load.
91
+ *
92
+ * @category Data Collection
93
+ * @defaultValue false
94
+ */
95
+ enableRemoteSdkConfiguration?: boolean | undefined
96
+ }
97
+
98
+ /**
99
+ * Function called before a Log event is sent to KubeSense. See {@link LogsInitConfiguration.beforeSend}
100
+ *
101
+ * @param event - The log event
102
+ * @param context - The log event domain context
103
+ * @returns true if the event should be sent to KubeSense, false otherwise
104
+ */
105
+ export type LogsBeforeSend = (event: LogsEvent, context: LogsEventDomainContext) => boolean
106
+
107
+ export const LOGS_SCHEMA = {
108
+ ...BROWSER_CORE_SCHEMA,
109
+ forwardErrorsToLogs: { type: 'boolean', default: true, strict: false },
110
+ enableRemoteSdkConfiguration: { type: 'boolean', default: false, strict: false },
111
+ forwardConsoleLogs: {
112
+ type: 'enum',
113
+ values: ConsoleApiName,
114
+ multiple: true,
115
+ allowAll: true,
116
+ default: [] as ConsoleApiName[],
117
+ },
118
+ forwardReports: {
119
+ type: 'enum',
120
+ values: RawReportType,
121
+ multiple: true,
122
+ allowAll: true,
123
+ default: [] as RawReportType[],
124
+ },
125
+ plugins: {
126
+ type: 'schema',
127
+ multiple: true,
128
+ strict: false,
129
+ default: [] as LogsPlugin[],
130
+ schema: {
131
+ name: { type: 'string', required: true },
132
+ getConfigurationTelemetry: {
133
+ type: 'function',
134
+ strict: false,
135
+ signature: undefined as LogsPlugin['getConfigurationTelemetry'],
136
+ },
137
+ onInit: { type: 'function', strict: false, signature: undefined as LogsPlugin['onInit'] },
138
+ onLogsStart: { type: 'function', strict: false, signature: undefined as LogsPlugin['onLogsStart'] },
139
+ },
140
+ },
141
+ } as const
142
+
143
+ export type LogsConfiguration = InferredConfig<typeof LOGS_SCHEMA>
144
+
145
+ export function validateAndBuildLogsConfiguration(
146
+ initConfiguration: LogsInitConfiguration
147
+ ): LogsConfiguration | undefined {
148
+ const config = validateAndBuildConfiguration(
149
+ initConfiguration as unknown as Record<string, unknown>,
150
+ LOGS_SCHEMA,
151
+ display
152
+ )
153
+ if (!config) {
154
+ return
155
+ }
156
+ return {
157
+ ...config,
158
+ beforeSend: config.beforeSend ? catchUserErrors(config.beforeSend as any, 'beforeSend threw an error:') : undefined,
159
+ }
160
+ }
161
+
162
+ export function serializeLogsConfiguration(configuration: LogsInitConfiguration) {
163
+ const baseSerializedInitConfiguration = serializeConfiguration(configuration)
164
+
165
+ return {
166
+ forward_errors_to_logs: configuration.forwardErrorsToLogs,
167
+ enable_remote_sdk_configuration: !!configuration.enableRemoteSdkConfiguration,
168
+ forward_console_logs: configuration.forwardConsoleLogs,
169
+ forward_reports: configuration.forwardReports,
170
+ plugins: configuration.plugins?.map((plugin) => ({
171
+ name: plugin.name,
172
+ ...plugin.getConfigurationTelemetry?.(),
173
+ })),
174
+ ...baseSerializedInitConfiguration,
175
+ } satisfies RawTelemetryConfiguration
176
+ }
@@ -0,0 +1,58 @@
1
+ import type { ClocksState } from '@kubesense/kubesense-js-core/time'
2
+ import { combine, ConsoleApiName } from '@kubesense/kubesense-js-core/util'
3
+ import type { Context, Observable, BufferedData } from '@kubesense/kubesense-browser-core'
4
+ import { timeStampNow } from '@kubesense/kubesense-js-core/time'
5
+ import { BufferedDataType, ErrorSource } from '@kubesense/kubesense-browser-core'
6
+ import type { LogsConfiguration } from '../configuration'
7
+ import type { LifeCycle, RawLogsEventCollectedData } from '../lifeCycle'
8
+ import { LifeCycleEventType } from '../lifeCycle'
9
+ import { StatusType } from '../logger/isAuthorized'
10
+ import type { RawLogsEvent } from '../../rawLogsEvent.types'
11
+ import { createErrorFieldFromRawError } from '../createErrorFieldFromRawError'
12
+
13
+ export interface ProvidedError {
14
+ startClocks: ClocksState
15
+ error: unknown
16
+ context?: Context
17
+ handlingStack: string
18
+ }
19
+
20
+ export const LogStatusForApi = {
21
+ [ConsoleApiName.log]: StatusType.info,
22
+ [ConsoleApiName.debug]: StatusType.debug,
23
+ [ConsoleApiName.info]: StatusType.info,
24
+ [ConsoleApiName.warn]: StatusType.warn,
25
+ [ConsoleApiName.error]: StatusType.error,
26
+ }
27
+
28
+ export function startConsoleCollection(
29
+ configuration: LogsConfiguration,
30
+ lifeCycle: LifeCycle,
31
+ bufferedDataObservable: Observable<BufferedData>
32
+ ) {
33
+ const subscription = bufferedDataObservable.subscribe(({ data: log, type }) => {
34
+ if (type !== BufferedDataType.CONSOLE || !configuration.forwardConsoleLogs.includes(log.api)) {
35
+ return
36
+ }
37
+ const collectedData: RawLogsEventCollectedData<RawLogsEvent> = {
38
+ rawLogsEvent: combine(
39
+ {
40
+ date: timeStampNow(),
41
+ message: log.message,
42
+ origin: ErrorSource.CONSOLE,
43
+ error: log.error && createErrorFieldFromRawError(log.error),
44
+ _kubesense: log.error && { debug_ids: log.error.debugIds },
45
+ status: LogStatusForApi[log.api],
46
+ },
47
+ log.error?.context
48
+ ),
49
+ domainContext: {
50
+ handlingStack: log.handlingStack,
51
+ },
52
+ }
53
+
54
+ lifeCycle.notify(LifeCycleEventType.RAW_LOG_COLLECTED, collectedData)
55
+ })
56
+
57
+ return { stop: () => subscription.unsubscribe() }
58
+ }
@@ -0,0 +1,15 @@
1
+ import { isWorkerEnvironment } from '@kubesense/kubesense-js-core/util'
2
+ import type { CommonContext } from '../../rawLogsEvent.types'
3
+
4
+ export function buildCommonContext(): CommonContext {
5
+ if (isWorkerEnvironment) {
6
+ return {}
7
+ }
8
+
9
+ return {
10
+ view: {
11
+ referrer: document.referrer,
12
+ url: window.location.href,
13
+ },
14
+ }
15
+ }
@@ -0,0 +1,19 @@
1
+ import type { RelativeTime } from '@kubesense/kubesense-js-core/time'
2
+ import type { SessionManager } from '@kubesense/kubesense-browser-core'
3
+
4
+ export interface InternalContext {
5
+ session_id: string | undefined
6
+ }
7
+
8
+ export function startInternalContext(sessionManager: SessionManager) {
9
+ return {
10
+ get: (startTime?: number): InternalContext | undefined => {
11
+ const trackedSession = sessionManager.findTrackedSession(startTime as RelativeTime)
12
+ if (trackedSession) {
13
+ return {
14
+ session_id: trackedSession.id,
15
+ }
16
+ }
17
+ },
18
+ }
19
+ }
@@ -0,0 +1,60 @@
1
+ import { globalObject } from '@kubesense/kubesense-js-core/util'
2
+ import type { RelativeTime } from '@kubesense/kubesense-js-core/time'
3
+ import type { RumInternalContext } from '@kubesense/kubesense-browser-core'
4
+ import { willSyntheticsInjectRum } from '@kubesense/kubesense-browser-core'
5
+ import { SKIPPED } from '@kubesense/kubesense-js-core/assembly'
6
+ import type { Hooks } from '../hooks'
7
+
8
+ interface Rum {
9
+ getInternalContext?: (startTime?: RelativeTime) => RumInternalContext | undefined
10
+ }
11
+
12
+ interface BrowserWindow {
13
+ KUBESENSE_SDK?: Rum
14
+ KUBESENSE_SDK_SYNTHETICS?: Rum
15
+ }
16
+
17
+ export function startRUMInternalContext(hooks: Hooks) {
18
+ const browserWindow = globalObject as BrowserWindow
19
+
20
+ hooks.assembleEventDefaults.register(({ startTime }) => {
21
+ const internalContext = getRUMInternalContext(startTime)
22
+ if (!internalContext) {
23
+ return SKIPPED
24
+ }
25
+
26
+ return internalContext
27
+ })
28
+
29
+ hooks.assembleTelemetry.register(({ startTime }) => {
30
+ const internalContext = getRUMInternalContext(startTime)
31
+
32
+ if (!internalContext) {
33
+ return SKIPPED
34
+ }
35
+
36
+ return {
37
+ application: { id: internalContext.application_id },
38
+ view: { id: internalContext.view?.id },
39
+ action: { id: internalContext.user_action?.id as string },
40
+ }
41
+ })
42
+
43
+ function getRUMInternalContext(startTime?: RelativeTime) {
44
+ const willSyntheticsInjectRumResult = willSyntheticsInjectRum()
45
+ const rumSource = willSyntheticsInjectRumResult
46
+ ? browserWindow.KUBESENSE_SDK_SYNTHETICS
47
+ : browserWindow.KUBESENSE_SDK
48
+ const rumContext = getInternalContextFromRumGlobal(startTime, rumSource)
49
+
50
+ if (rumContext) {
51
+ return rumContext
52
+ }
53
+ }
54
+
55
+ function getInternalContextFromRumGlobal(startTime?: RelativeTime, rumGlobal?: Rum): RumInternalContext | undefined {
56
+ if (rumGlobal?.getInternalContext) {
57
+ return rumGlobal.getInternalContext(startTime)
58
+ }
59
+ }
60
+ }
@@ -0,0 +1,34 @@
1
+ import type { SessionManager } from '@kubesense/kubesense-browser-core'
2
+ import { DISCARDED } from '@kubesense/kubesense-js-core/assembly'
3
+ import type { LogsConfiguration } from '../configuration'
4
+ import type { AssembleHook } from '../hooks'
5
+
6
+ export function startSessionContext(
7
+ hook: AssembleHook,
8
+ configuration: LogsConfiguration,
9
+ sessionManager: SessionManager
10
+ ) {
11
+ hook.register(({ startTime }) => {
12
+ // Used to attach a (fresh, safe-to-reference) session id: subject to the default
13
+ // TRACKED_SESSION_MAX_AGE, so it becomes undefined once the session is too old to reference.
14
+ const session = sessionManager.findTrackedSession(startTime)
15
+
16
+ // Used for the discard decision: unlike `session` above, this ignores session age (logs
17
+ // should keep being sent indefinitely, with or without a session, once a session was
18
+ // legitimately tracked here) but still respects sampling.
19
+ const isSessionTracked = sessionManager.findTrackedSession(startTime, {
20
+ returnInactive: true,
21
+ maxAge: Infinity,
22
+ })
23
+
24
+ if (!isSessionTracked) {
25
+ return DISCARDED
26
+ }
27
+
28
+ return {
29
+ service: configuration.service,
30
+ session_id: session ? session.id : undefined,
31
+ session: session ? { id: session.id } : undefined,
32
+ }
33
+ })
34
+ }
@@ -0,0 +1,18 @@
1
+ import { DISCARDED, SKIPPED } from '@kubesense/kubesense-js-core/assembly'
2
+ import type { TrackingConsentState } from '@kubesense/kubesense-browser-core'
3
+ import type { Hooks } from '../hooks'
4
+
5
+ export function startTrackingConsentContext(hooks: Hooks, trackingConsentState: TrackingConsentState) {
6
+ function isConsented() {
7
+ const wasConsented = trackingConsentState.isGranted()
8
+
9
+ if (!wasConsented) {
10
+ return DISCARDED
11
+ }
12
+
13
+ return SKIPPED
14
+ }
15
+
16
+ hooks.assembleEventDefaults.register(isConsented)
17
+ hooks.assembleTelemetry.register(isConsented)
18
+ }
@@ -0,0 +1,22 @@
1
+ import type { RawError } from '@kubesense/kubesense-browser-core'
2
+ import type { RawLoggerLogsEvent } from '../rawLogsEvent.types'
3
+
4
+ export function createErrorFieldFromRawError(
5
+ rawError: RawError,
6
+ {
7
+ /**
8
+ * Set this to `true` to include the error message in the error field. In most cases, the error
9
+ * message is already included in the log message, so we don't need to include it again.
10
+ */
11
+ includeMessage = false,
12
+ } = {}
13
+ ): NonNullable<RawLoggerLogsEvent['error']> {
14
+ return {
15
+ stack: rawError.stack,
16
+ kind: rawError.type,
17
+ message: includeMessage ? rawError.message : undefined,
18
+ causes: rawError.causes,
19
+ fingerprint: rawError.fingerprint,
20
+ handling: rawError.handling,
21
+ }
22
+ }
@@ -0,0 +1,40 @@
1
+ import type { RelativeTime } from '@kubesense/kubesense-js-core/time'
2
+ import type { TelemetryEvent } from '@kubesense/kubesense-browser-core'
3
+ import { createHook } from '@kubesense/kubesense-js-core/assembly'
4
+ import type { Hook } from '@kubesense/kubesense-js-core/assembly'
5
+ import type { RecursivePartial } from '@kubesense/kubesense-js-core/util'
6
+ import type { LogsEvent } from '../logsEvent.types'
7
+ import type { RawLogsEvent } from '../rawLogsEvent.types'
8
+ import type { LogsEventDomainContext } from '../domainContext.types'
9
+
10
+ export type DefaultLogsEventAttributes = RecursivePartial<LogsEvent>
11
+ export type DefaultTelemetryEventAttributes = RecursivePartial<TelemetryEvent>
12
+
13
+ type DeepReadonly<T> = {
14
+ readonly [K in keyof T]: DeepReadonly<T[K]>
15
+ }
16
+
17
+ // Use readonly and DeepReadonly to prevent assemble hook callbacks from mutating the inputs.
18
+ // DeepReadonly is only applied to objects rather than the entire AssembleHookParams to avoid casts for primitives.
19
+ export interface AssembleHookParams {
20
+ readonly startTime: RelativeTime
21
+ rawLogsEvent: DeepReadonly<RawLogsEvent>
22
+ domainContext: DeepReadonly<LogsEventDomainContext>
23
+ }
24
+
25
+ export type AssembleHook = Hook<AssembleHookParams, DefaultLogsEventAttributes>
26
+ export type AssembleTelemetryHook = Hook<{ startTime: RelativeTime }, DefaultTelemetryEventAttributes>
27
+
28
+ export interface Hooks {
29
+ assembleEventDefaults: AssembleHook
30
+ assembleTelemetry: AssembleTelemetryHook
31
+ assembleEvent: AssembleHook
32
+ }
33
+
34
+ export function createHooks(): Hooks {
35
+ return {
36
+ assembleEventDefaults: createHook(),
37
+ assembleTelemetry: createHook(),
38
+ assembleEvent: createHook(),
39
+ }
40
+ }
@@ -0,0 +1,25 @@
1
+ import { AbstractLifeCycle } from '@kubesense/kubesense-browser-core'
2
+ import type { Context } from '@kubesense/kubesense-browser-core'
3
+ import type { LogsEvent } from '../logsEvent.types'
4
+ import type { CommonContext, RawLogsEvent } from '../rawLogsEvent.types'
5
+ import type { LogsEventDomainContext } from '../domainContext.types'
6
+
7
+ export const enum LifeCycleEventType {
8
+ RAW_LOG_COLLECTED,
9
+ LOG_COLLECTED,
10
+ }
11
+
12
+ interface LifeCycleEventMap {
13
+ [LifeCycleEventType.RAW_LOG_COLLECTED]: RawLogsEventCollectedData
14
+ [LifeCycleEventType.LOG_COLLECTED]: LogsEvent & Context
15
+ }
16
+
17
+ export const LifeCycle = AbstractLifeCycle<LifeCycleEventMap>
18
+ export type LifeCycle = AbstractLifeCycle<LifeCycleEventMap>
19
+
20
+ export interface RawLogsEventCollectedData<E extends RawLogsEvent = RawLogsEvent> {
21
+ rawLogsEvent: E
22
+ savedCommonContext?: CommonContext
23
+ domainContext?: LogsEventDomainContext<E['origin']>
24
+ ktags?: string[]
25
+ }
@@ -0,0 +1,33 @@
1
+ import type { Logger, HandlerType } from '../logger'
2
+
3
+ export function isAuthorized(status: StatusType, handlerType: HandlerType, logger: Logger) {
4
+ const loggerHandler = logger.getHandler()
5
+ const sanitizedHandlerType = Array.isArray(loggerHandler) ? loggerHandler : [loggerHandler]
6
+ return STATUS_PRIORITIES[status] >= STATUS_PRIORITIES[logger.getLevel()] && sanitizedHandlerType.includes(handlerType)
7
+ }
8
+
9
+ export const StatusType = {
10
+ ok: 'ok',
11
+ debug: 'debug',
12
+ info: 'info',
13
+ notice: 'notice',
14
+ warn: 'warn',
15
+ error: 'error',
16
+ critical: 'critical',
17
+ alert: 'alert',
18
+ emerg: 'emerg',
19
+ } as const
20
+
21
+ export const STATUS_PRIORITIES: { [key in StatusType]: number } = {
22
+ [StatusType.ok]: 0,
23
+ [StatusType.debug]: 1,
24
+ [StatusType.info]: 2,
25
+ [StatusType.notice]: 4,
26
+ [StatusType.warn]: 5,
27
+ [StatusType.error]: 6,
28
+ [StatusType.critical]: 7,
29
+ [StatusType.alert]: 8,
30
+ [StatusType.emerg]: 9,
31
+ }
32
+
33
+ export type StatusType = (typeof StatusType)[keyof typeof StatusType]