securenow 4.0.12 → 5.0.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.
package/tracing.d.ts CHANGED
@@ -1,89 +1,182 @@
1
- /**
2
- * SecureNow Tracing Module TypeScript Declarations
3
- *
4
- * Core tracing functionality for Node.js applications.
5
- * This is typically loaded via register.js, not imported directly.
6
- */
7
-
8
- /**
9
- * Default sensitive fields that are automatically redacted from request bodies
10
- */
11
- export const DEFAULT_SENSITIVE_FIELDS: readonly string[];
12
-
13
- /**
14
- * Redact sensitive fields from an object (recursively)
15
- *
16
- * @param obj - Object to redact (can be nested)
17
- * @param sensitiveFields - Array of field names to redact (case-insensitive substring match)
18
- * @returns Redacted copy of the object
19
- *
20
- * @example
21
- * ```typescript
22
- * import { redactSensitiveData } from 'securenow/tracing';
23
- *
24
- * const data = {
25
- * email: 'user@example.com',
26
- * password: 'secret123',
27
- * nested: {
28
- * api_key: 'sk_live_abc123'
29
- * }
30
- * };
31
- *
32
- * const redacted = redactSensitiveData(data);
33
- * // Result: {
34
- * // email: 'user@example.com',
35
- * // password: '[REDACTED]',
36
- * // nested: { api_key: '[REDACTED]' }
37
- * // }
38
- * ```
39
- */
40
- export function redactSensitiveData<T = any>(
41
- obj: T,
42
- sensitiveFields?: string[]
43
- ): T;
44
-
45
- /**
46
- * Redact sensitive data from GraphQL query strings
47
- *
48
- * @param query - GraphQL query string
49
- * @param sensitiveFields - Array of field names to redact
50
- * @returns Redacted query string
51
- *
52
- * @example
53
- * ```typescript
54
- * import { redactGraphQLQuery } from 'securenow/tracing';
55
- *
56
- * const query = `
57
- * mutation {
58
- * login(email: "user@example.com", password: "secret123") {
59
- * token
60
- * }
61
- * }
62
- * `;
63
- *
64
- * const redacted = redactGraphQLQuery(query);
65
- * // Result: mutation { login(email: "user@example.com", password: "[REDACTED]") { token } }
66
- * ```
67
- */
68
- export function redactGraphQLQuery(
69
- query: string,
70
- sensitiveFields?: string[]
71
- ): string;
72
-
73
- /**
74
- * Environment Variables (same as register.js):
75
- *
76
- * Required:
77
- * - SECURENOW_APPID=your-app-name
78
- * - SECURENOW_INSTANCE=http://host:4318
79
- *
80
- * Optional:
81
- * - SECURENOW_NO_UUID=1
82
- * - SECURENOW_STRICT=1
83
- * - SECURENOW_CAPTURE_BODY=1
84
- * - SECURENOW_MAX_BODY_SIZE=10240
85
- * - SECURENOW_SENSITIVE_FIELDS=field1,field2
86
- * - SECURENOW_DISABLE_INSTRUMENTATIONS=pkg1,pkg2
87
- * - OTEL_LOG_LEVEL=info|debug
88
- * - SECURENOW_TEST_SPAN=1
89
- */
1
+ /**
2
+ * SecureNow Tracing Module TypeScript Declarations
3
+ *
4
+ * Core tracing functionality for Node.js applications.
5
+ * This is typically loaded via register.js, not imported directly.
6
+ */
7
+
8
+ /**
9
+ * Default sensitive fields that are automatically redacted from request bodies
10
+ */
11
+ export const DEFAULT_SENSITIVE_FIELDS: readonly string[];
12
+
13
+ /**
14
+ * Redact sensitive fields from an object (recursively)
15
+ *
16
+ * @param obj - Object to redact (can be nested)
17
+ * @param sensitiveFields - Array of field names to redact (case-insensitive substring match)
18
+ * @returns Redacted copy of the object
19
+ *
20
+ * @example
21
+ * ```typescript
22
+ * import { redactSensitiveData } from 'securenow/tracing';
23
+ *
24
+ * const data = {
25
+ * email: 'user@example.com',
26
+ * password: 'secret123',
27
+ * nested: {
28
+ * api_key: 'sk_live_abc123'
29
+ * }
30
+ * };
31
+ *
32
+ * const redacted = redactSensitiveData(data);
33
+ * // Result: {
34
+ * // email: 'user@example.com',
35
+ * // password: '[REDACTED]',
36
+ * // nested: { api_key: '[REDACTED]' }
37
+ * // }
38
+ * ```
39
+ */
40
+ export function redactSensitiveData<T = any>(
41
+ obj: T,
42
+ sensitiveFields?: string[]
43
+ ): T;
44
+
45
+ /**
46
+ * Redact sensitive data from GraphQL query strings
47
+ *
48
+ * @param query - GraphQL query string
49
+ * @param sensitiveFields - Array of field names to redact
50
+ * @returns Redacted query string
51
+ *
52
+ * @example
53
+ * ```typescript
54
+ * import { redactGraphQLQuery } from 'securenow/tracing';
55
+ *
56
+ * const query = `
57
+ * mutation {
58
+ * login(email: "user@example.com", password: "secret123") {
59
+ * token
60
+ * }
61
+ * }
62
+ * `;
63
+ *
64
+ * const redacted = redactGraphQLQuery(query);
65
+ * // Result: mutation { login(email: "user@example.com", password: "[REDACTED]") { token } }
66
+ * ```
67
+ */
68
+ export function redactGraphQLQuery(
69
+ query: string,
70
+ sensitiveFields?: string[]
71
+ ): string;
72
+
73
+ /**
74
+ * OpenTelemetry Logger interface
75
+ */
76
+ export interface Logger {
77
+ emit(logRecord: LogRecord): void;
78
+ }
79
+
80
+ /**
81
+ * OpenTelemetry LogRecord interface
82
+ */
83
+ export interface LogRecord {
84
+ /**
85
+ * Severity number (OpenTelemetry standard)
86
+ * 5 = DEBUG, 9 = INFO, 13 = WARN, 17 = ERROR
87
+ */
88
+ severityNumber: number;
89
+
90
+ /**
91
+ * Human-readable severity text
92
+ */
93
+ severityText: 'DEBUG' | 'INFO' | 'WARN' | 'ERROR' | string;
94
+
95
+ /**
96
+ * Log message body
97
+ */
98
+ body: string;
99
+
100
+ /**
101
+ * Structured attributes for filtering/searching
102
+ */
103
+ attributes?: Record<string, any>;
104
+ }
105
+
106
+ /**
107
+ * OpenTelemetry LoggerProvider interface
108
+ */
109
+ export interface LoggerProvider {
110
+ getLogger(name: string, version?: string): Logger;
111
+ shutdown?(): Promise<void> | void;
112
+ }
113
+
114
+ /**
115
+ * Get a logger instance for sending structured logs to SigNoz
116
+ *
117
+ * @param name - Logger name (e.g., 'my-service', 'auth-module')
118
+ * @param version - Logger version (optional, defaults to '1.0.0')
119
+ * @returns Logger instance or null if logging is not enabled
120
+ *
121
+ * @example
122
+ * ```typescript
123
+ * import { getLogger } from 'securenow/tracing';
124
+ *
125
+ * const logger = getLogger('my-service', '1.0.0');
126
+ *
127
+ * if (logger) {
128
+ * logger.emit({
129
+ * severityNumber: 9,
130
+ * severityText: 'INFO',
131
+ * body: 'User logged in',
132
+ * attributes: {
133
+ * userId: 123,
134
+ * username: 'john',
135
+ * },
136
+ * });
137
+ * }
138
+ * ```
139
+ */
140
+ export function getLogger(name?: string, version?: string): Logger | null;
141
+
142
+ /**
143
+ * Check if logging is enabled
144
+ *
145
+ * @returns true if SECURENOW_LOGGING_ENABLED=1, false otherwise
146
+ *
147
+ * @example
148
+ * ```typescript
149
+ * import { isLoggingEnabled } from 'securenow/tracing';
150
+ *
151
+ * if (isLoggingEnabled()) {
152
+ * console.log('Logging is enabled');
153
+ * }
154
+ * ```
155
+ */
156
+ export function isLoggingEnabled(): boolean;
157
+
158
+ /**
159
+ * The OpenTelemetry LoggerProvider instance (if logging is enabled)
160
+ * Use getLogger() instead of accessing this directly
161
+ */
162
+ export const loggerProvider: LoggerProvider | null;
163
+
164
+ /**
165
+ * Environment Variables (same as register.js):
166
+ *
167
+ * Required:
168
+ * - SECURENOW_APPID=your-app-name
169
+ * - SECURENOW_INSTANCE=http://host:4318
170
+ *
171
+ * Optional:
172
+ * - SECURENOW_LOGGING_ENABLED=1 # Enable logging (default: 1)
173
+ * - SECURENOW_NO_UUID=1
174
+ * - SECURENOW_STRICT=1
175
+ * - SECURENOW_CAPTURE_BODY=1
176
+ * - SECURENOW_MAX_BODY_SIZE=10240
177
+ * - SECURENOW_SENSITIVE_FIELDS=field1,field2
178
+ * - SECURENOW_DISABLE_INSTRUMENTATIONS=pkg1,pkg2
179
+ * - OTEL_LOG_LEVEL=info|debug
180
+ * - SECURENOW_TEST_SPAN=1
181
+ * - OTEL_EXPORTER_OTLP_LOGS_ENDPOINT=... # Override logs endpoint
182
+ */
package/tracing.js CHANGED
@@ -6,7 +6,7 @@
6
6
  * Env:
7
7
  * SECURENOW_APPID=logical-name # or OTEL_SERVICE_NAME=logical-name
8
8
  * SECURENOW_NO_UUID=1 # one service.name across all workers
9
- * SECURENOW_INSTANCE=http://host:4318 # OTLP/HTTP base (default http://46.62.173.237:4318)
9
+ * SECURENOW_INSTANCE=http://host:4318 # OTLP/HTTP base (default https://freetrial.securenow.ai:4318)
10
10
  * OTEL_EXPORTER_OTLP_ENDPOINT=... # alternative base
11
11
  * OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=... # full traces URL
12
12
  * OTEL_EXPORTER_OTLP_HEADERS="k=v,k2=v2"
@@ -21,6 +21,8 @@
21
21
  const { diag, DiagConsoleLogger, DiagLogLevel } = require('@opentelemetry/api');
22
22
  const { NodeSDK } = require('@opentelemetry/sdk-node');
23
23
  const { OTLPTraceExporter } = require('@opentelemetry/exporter-trace-otlp-http');
24
+ const { OTLPLogExporter } = require('@opentelemetry/exporter-logs-otlp-http');
25
+ const { LoggerProvider, BatchLogRecordProcessor } = require('@opentelemetry/sdk-logs');
24
26
  const { Resource } = require('@opentelemetry/resources');
25
27
  const { SemanticResourceAttributes } = require('@opentelemetry/semantic-conventions');
26
28
  const { getNodeAutoInstrumentations } = require('@opentelemetry/auto-instrumentations-node');
@@ -107,8 +109,9 @@ function redactGraphQLQuery(query, sensitiveFields = DEFAULT_SENSITIVE_FIELDS) {
107
109
  })();
108
110
 
109
111
  // -------- endpoints --------
110
- const endpointBase = (env('SECURENOW_INSTANCE') || env('OTEL_EXPORTER_OTLP_ENDPOINT') || 'http://46.62.173.237:4318').replace(/\/$/, '');
112
+ const endpointBase = (env('SECURENOW_INSTANCE') || env('OTEL_EXPORTER_OTLP_ENDPOINT') || 'https://freetrial.securenow.ai:4318').replace(/\/$/, '');
111
113
  const tracesUrl = env('OTEL_EXPORTER_OTLP_TRACES_ENDPOINT') || `${endpointBase}/v1/traces`;
114
+ const logsUrl = env('OTEL_EXPORTER_OTLP_LOGS_ENDPOINT') || `${endpointBase}/v1/logs`;
112
115
  const headers = parseHeaders(env('OTEL_EXPORTER_OTLP_HEADERS'));
113
116
 
114
117
  // -------- naming rules --------
@@ -241,6 +244,35 @@ const httpInstrumentation = new HttpInstrumentation({
241
244
  },
242
245
  });
243
246
 
247
+ // -------- Logging Configuration --------
248
+ const loggingEnabled = String(env('SECURENOW_LOGGING_ENABLED')) !== '0' && String(env('SECURENOW_LOGGING_ENABLED')).toLowerCase() !== 'false';
249
+
250
+ // Create shared resource for both traces and logs
251
+ const sharedResource = new Resource({
252
+ [SemanticResourceAttributes.SERVICE_NAME]: serviceName,
253
+ [SemanticResourceAttributes.SERVICE_INSTANCE_ID]: serviceInstanceId,
254
+ [SemanticResourceAttributes.DEPLOYMENT_ENVIRONMENT]: env('NODE_ENV') || 'production',
255
+ [SemanticResourceAttributes.SERVICE_VERSION]: process.env.npm_package_version || undefined,
256
+ });
257
+
258
+ // Initialize LoggerProvider if logging is enabled
259
+ let loggerProvider = null;
260
+ let globalLogger = null;
261
+
262
+ if (loggingEnabled) {
263
+ const logExporter = new OTLPLogExporter({
264
+ url: logsUrl,
265
+ headers
266
+ });
267
+
268
+ loggerProvider = new LoggerProvider({
269
+ resource: sharedResource,
270
+ processors: [new BatchLogRecordProcessor(logExporter)],
271
+ });
272
+
273
+ globalLogger = loggerProvider.getLogger('securenow', '1.0.0');
274
+ }
275
+
244
276
  // -------- SDK --------
245
277
  const traceExporter = new OTLPTraceExporter({ url: tracesUrl, headers });
246
278
  const sdk = new NodeSDK({
@@ -252,12 +284,7 @@ const sdk = new NodeSDK({
252
284
  '@opentelemetry/instrumentation-http': { enabled: false }, // We use our custom one above
253
285
  }),
254
286
  ],
255
- resource: new Resource({
256
- [SemanticResourceAttributes.SERVICE_NAME]: serviceName,
257
- [SemanticResourceAttributes.SERVICE_INSTANCE_ID]: serviceInstanceId,
258
- [SemanticResourceAttributes.DEPLOYMENT_ENVIRONMENT]: env('NODE_ENV') || 'production',
259
- [SemanticResourceAttributes.SERVICE_VERSION]: process.env.npm_package_version || undefined,
260
- }),
287
+ resource: sharedResource,
261
288
  });
262
289
 
263
290
  // -------- start / shutdown (sync/async safe) --------
@@ -265,6 +292,11 @@ const sdk = new NodeSDK({
265
292
  try {
266
293
  await Promise.resolve(sdk.start?.());
267
294
  console.log('[securenow] OTel SDK started → %s', tracesUrl);
295
+ if (loggingEnabled) {
296
+ console.log('[securenow] 📋 Logging: ENABLED → %s', logsUrl);
297
+ } else {
298
+ console.log('[securenow] 📋 Logging: DISABLED (set SECURENOW_LOGGING_ENABLED=1 to enable)');
299
+ }
268
300
  if (captureBody) {
269
301
  console.log('[securenow] 📝 Request body capture: ENABLED (max: %d bytes, redacting %d sensitive fields)', maxBodySize, allSensitiveFields.length);
270
302
  }
@@ -279,9 +311,28 @@ const sdk = new NodeSDK({
279
311
  })();
280
312
 
281
313
  async function safeShutdown(sig) {
282
- try { await Promise.resolve(sdk.shutdown?.()); console.log(`[securenow] Tracing terminated on ${sig}`); }
283
- catch (e) { console.error('[securenow] Tracing shutdown error:', e); }
314
+ try {
315
+ await Promise.resolve(sdk.shutdown?.());
316
+ if (loggerProvider) {
317
+ await Promise.resolve(loggerProvider.shutdown?.());
318
+ }
319
+ console.log(`[securenow] Tracing and logging terminated on ${sig}`);
320
+ }
321
+ catch (e) { console.error('[securenow] Shutdown error:', e); }
284
322
  finally { process.exit(0); }
285
323
  }
286
324
  process.on('SIGINT', () => safeShutdown('SIGINT'));
287
325
  process.on('SIGTERM', () => safeShutdown('SIGTERM'));
326
+
327
+ // -------- Export logger for consuming applications --------
328
+ module.exports = {
329
+ loggerProvider,
330
+ getLogger: (name = 'default', version = '1.0.0') => {
331
+ if (!loggerProvider) {
332
+ console.warn('[securenow] Logging is not enabled. Set SECURENOW_LOGGING_ENABLED=1 to enable logging.');
333
+ return null;
334
+ }
335
+ return loggerProvider.getLogger(name, version);
336
+ },
337
+ isLoggingEnabled: () => loggingEnabled,
338
+ };