@digitraffic/common 2023.3.27-1 → 2023.4.4-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/README.md CHANGED
@@ -15,7 +15,7 @@ In package.json dependencies:
15
15
  In code:
16
16
 
17
17
  ```
18
- import {DigitrafficStack, StackConfiguration} from "@digitraffic/common/aws/infra/stack/stack";
18
+ import {DigitrafficStack, StackConfiguration} from "@digitraffic/common/dist/aws/infra/stack/stack";
19
19
  ```
20
20
 
21
21
  ### DigitrafficStack
@@ -1,7 +1,7 @@
1
1
  import { DtLogger } from "../../runtime/dt-logger";
2
2
  import { LambdaResponse } from "../../types/lambda-response";
3
- export declare type LoggingHandler = (method: () => Promise<LambdaResponse>, logger: DtLogger) => Promise<LambdaResponse>;
4
- export declare type ErrorHandler = (error: unknown, logger: DtLogger) => LambdaResponse;
3
+ export type LoggingHandler = (method: () => Promise<LambdaResponse>, logger: DtLogger) => Promise<LambdaResponse>;
4
+ export type ErrorHandler = (error: unknown, logger: DtLogger) => LambdaResponse;
5
5
  /**
6
6
  * Factory class for creating lambda-handler functions. You can set functionality to handle logging and error-handling,
7
7
  * with the defaults:
@@ -1,7 +1,7 @@
1
1
  import { IntegrationResponse, LambdaIntegration } from "aws-cdk-lib/aws-apigateway";
2
2
  import { IFunction } from "aws-cdk-lib/aws-lambda";
3
3
  import { MediaType } from "../../types/mediatypes";
4
- declare type ParameterType = "path" | "querystring" | "context";
4
+ type ParameterType = "path" | "querystring" | "context";
5
5
  interface ApiParameter {
6
6
  type: ParameterType;
7
7
  name: string;
@@ -1,6 +1,6 @@
1
1
  import { Schedule } from "@aws-cdk/aws-synthetics-alpha";
2
2
  /** Optional env parameters for canary */
3
- declare type CanaryEnv = Record<string, string>;
3
+ type CanaryEnv = Record<string, string>;
4
4
  export interface CanaryParameters {
5
5
  readonly name: string;
6
6
  readonly schedule?: Schedule;
@@ -3,8 +3,8 @@ import { IncomingMessage } from "http";
3
3
  import { MediaType } from "../../types/mediatypes";
4
4
  import { FeatureCollection } from "geojson";
5
5
  export declare const API_KEY_HEADER = "x-api-key";
6
- declare type CheckerFunction = (Res: IncomingMessage) => Promise<void>;
7
- declare type JsonCheckerFunction<T> = (json: T, body: string, message: IncomingMessage) => Promise<void>;
6
+ type CheckerFunction = (Res: IncomingMessage) => Promise<void>;
7
+ type JsonCheckerFunction<T> = (json: T, body: string, message: IncomingMessage) => Promise<void>;
8
8
  export declare class UrlChecker {
9
9
  private readonly requestOptions;
10
10
  constructor(hostname: string, apiKey?: string);
@@ -3,8 +3,8 @@ import { IVpc, SubnetSelection } from "aws-cdk-lib/aws-ec2";
3
3
  import { Role } from "aws-cdk-lib/aws-iam";
4
4
  import { DigitrafficStack } from "./stack";
5
5
  import { MonitoredFunctionAlarmProps } from "./monitoredfunction";
6
- export declare type LambdaEnvironment = Record<string, string>;
7
- export declare type DBLambdaEnvironment = LambdaEnvironment & {
6
+ export type LambdaEnvironment = Record<string, string>;
7
+ export type DBLambdaEnvironment = LambdaEnvironment & {
8
8
  SECRET_ID?: string;
9
9
  DB_APPLICATION: string;
10
10
  };
@@ -27,7 +27,7 @@ export interface FunctionParameters {
27
27
  architecture?: Architecture;
28
28
  singleLambda?: boolean;
29
29
  }
30
- export declare type MonitoredFunctionParameters = FunctionParameters & {
30
+ export type MonitoredFunctionParameters = FunctionParameters & {
31
31
  readonly durationAlarmProps?: MonitoredFunctionAlarmProps;
32
32
  readonly durationWarningProps?: MonitoredFunctionAlarmProps;
33
33
  readonly errorAlarmProps?: MonitoredFunctionAlarmProps;
@@ -11,6 +11,39 @@ const subscription_1 = require("../stack/subscription");
11
11
  * Creates a Lambda function that monitors default CloudWatch Lambda metrics with CloudWatch Alarms.
12
12
  */
13
13
  class MonitoredFunction extends aws_lambda_1.Function {
14
+ /**
15
+ * Create new MonitoredFunction. Use topics from given DigitrafficStack.
16
+ *
17
+ * @param stack DigitrafficStack
18
+ * @param id Lambda construct Id
19
+ * @param functionProps Lambda function properties
20
+ * @param props Monitored function properties
21
+ */
22
+ static create(stack, id, functionProps, props) {
23
+ if (props === MonitoredFunction.DISABLE_ALARMS &&
24
+ stack.configuration.production) {
25
+ throw new Error(
26
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
27
+ `Function ${functionProps.functionName} has DISABLE_ALARMS. Remove before installing to production or define your own properties!`);
28
+ }
29
+ return new MonitoredFunction(stack, id, functionProps, stack.alarmTopic, stack.warningTopic, stack.configuration.production, stack.configuration.trafficType, props);
30
+ }
31
+ /**
32
+ * Create new MonitoredFunction. Use topics from given DigitrafficStack. Generate names from given name and configuration shortName.
33
+ *
34
+ * For example, shortName FOO and given name update-things will create function FOO-UpdateThings and use code from lambda/update-things/update-things.ts method handler.
35
+ *
36
+ * @param stack DigitrafficStack
37
+ * @param name param-case name
38
+ * @param environment Lambda environment
39
+ * @param functionParameters Lambda function parameters
40
+ */
41
+ static createV2(stack, name, environment, functionParameters) {
42
+ const functionName = functionParameters?.functionName ??
43
+ `${stack.configuration.shortName}-${(0, change_case_1.pascalCase)(name)}`;
44
+ const functionProps = (0, lambda_configs_1.databaseFunctionProps)(stack, environment, functionName, name, functionParameters);
45
+ return MonitoredFunction.create(stack, functionName, functionProps, functionParameters);
46
+ }
14
47
  /**
15
48
  * @param scope Stack
16
49
  * @param id Lambda construct Id
@@ -48,39 +81,6 @@ class MonitoredFunction extends aws_lambda_1.Function {
48
81
  this.createAlarm(scope, this.metricThrottles(), "Throttles", "Throttles alarm", "Has throttled", trafficType, this.getAlarmActionForEnv(alarmSnsAction, warningSnsAction, production), 0, 1, 1, aws_cloudwatch_1.ComparisonOperator.GREATER_THAN_THRESHOLD, props?.throttleAlarmProps);
49
82
  }
50
83
  }
51
- /**
52
- * Create new MonitoredFunction. Use topics from given DigitrafficStack.
53
- *
54
- * @param stack DigitrafficStack
55
- * @param id Lambda construct Id
56
- * @param functionProps Lambda function properties
57
- * @param props Monitored function properties
58
- */
59
- static create(stack, id, functionProps, props) {
60
- if (props === MonitoredFunction.DISABLE_ALARMS &&
61
- stack.configuration.production) {
62
- throw new Error(
63
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
64
- `Function ${functionProps.functionName} has DISABLE_ALARMS. Remove before installing to production or define your own properties!`);
65
- }
66
- return new MonitoredFunction(stack, id, functionProps, stack.alarmTopic, stack.warningTopic, stack.configuration.production, stack.configuration.trafficType, props);
67
- }
68
- /**
69
- * Create new MonitoredFunction. Use topics from given DigitrafficStack. Generate names from given name and configuration shortName.
70
- *
71
- * For example, shortName FOO and given name update-things will create function FOO-UpdateThings and use code from lambda/update-things/update-things.ts method handler.
72
- *
73
- * @param stack DigitrafficStack
74
- * @param name param-case name
75
- * @param environment Lambda environment
76
- * @param functionParameters Lambda function parameters
77
- */
78
- static createV2(stack, name, environment, functionParameters) {
79
- const functionName = functionParameters?.functionName ??
80
- `${stack.configuration.shortName}-${(0, change_case_1.pascalCase)(name)}`;
81
- const functionProps = (0, lambda_configs_1.databaseFunctionProps)(stack, environment, functionName, name, functionParameters);
82
- return MonitoredFunction.create(stack, functionName, functionProps, functionParameters);
83
- }
84
84
  createAlarm(stack, metric, alarmId, alarmName, alarmDescription, trafficType, alarmSnsAction, threshold, evaluationPeriods, datapointsToAlarm, comparisonOperator, alarmProps) {
85
85
  metric
86
86
  .createAlarm(stack, `${this.node.id}-${alarmId}`, {
@@ -1,34 +1,60 @@
1
1
  /// <reference types="node" />
2
2
  import { Writable } from "stream";
3
- declare type LOG_LEVEL = "DEBUG" | "INFO" | "WARN" | "ERROR";
3
+ /** Logging level */
4
+ export type LOG_LEVEL = "DEBUG" | "INFO" | "WARN" | "ERROR";
5
+ /**
6
+ * Configuration object for configuring the Digitraffic logging utility
7
+ * @see {@link DtLogger}
8
+ */
4
9
  export interface LoggerConfiguration {
10
+ /** Name of the lambda */
5
11
  lambdaName?: string;
12
+ /** The file name where the logging occurs */
6
13
  fileName?: string;
14
+ /** The lambda runtime environment */
7
15
  runTime?: string;
16
+ /** Custom end point to write the logs to */
8
17
  writeStream?: Writable;
9
18
  }
10
- export interface LoggableType {
11
- /** name of method logging the message */
19
+ /**
20
+ * CustomParams allows to add keys prefixed with `custom` keyword to be added to an
21
+ * object.
22
+ */
23
+ export interface CustomParams {
24
+ /** do not log your apikey! */
25
+ customApikey?: never;
26
+ /** do not log your apikey! */
27
+ customApiKey?: never;
28
+ [key: `custom${Capitalize<string>}Count`]: number;
29
+ [key: `custom${Capitalize<string>}`]: string | number | boolean | Date | null | undefined;
30
+ }
31
+ /**
32
+ * Digitraffic logging object.
33
+ *
34
+ * `method` property is the only required propetry. {@link CustomParams} can be added by
35
+ * prefixin the property with keyword `custom`. The prefix is removed before writing to
36
+ * logging end point.
37
+ *
38
+ * @see {@link CustomParams}
39
+ */
40
+ export interface LoggableType extends CustomParams {
41
+ /** Name of the method logging the message */
12
42
  method: `${string}.${string}`;
13
- /** message to log, optional */
43
+ /** Message to log, optional */
14
44
  message?: string;
15
- /** type of message, optional */
45
+ /** Type of message, optional */
16
46
  type?: string;
17
- /** stack trace, optional */
18
- stack?: string;
19
- /** amount of time some operation took in milliseconds, optional */
47
+ /** Stack trace, optional */
48
+ stack?: string | undefined;
49
+ /** Amount of time some operation took in milliseconds, optional */
20
50
  tookMs?: number;
21
- /** count of something, optional */
22
- count?: number;
23
- /** do not log your apikey! */
24
- apikey?: never;
25
- /** do not log your apikey! */
26
- apiKey?: never;
27
- /** any other loggable key */
28
- [key: string]: string | number | boolean | Date | undefined;
51
+ /** Pass error object, which will be stringified before logging */
52
+ error?: unknown;
29
53
  }
30
54
  /**
31
- * Helper class for json-logging. Logged line will include
55
+ * Helper class for json-logging.
56
+ *
57
+ * Logged line will include:
32
58
  * * log-level
33
59
  * * lambdaName (taken from process environment)
34
60
  * * runtime (taken from process environment)
@@ -36,48 +62,55 @@ export interface LoggableType {
36
62
  */
37
63
  export declare class DtLogger {
38
64
  readonly lambdaName?: string;
39
- readonly fileName?: string;
40
65
  readonly runtime?: string;
41
66
  readonly writeStream: Writable;
67
+ /**
68
+ * Create a new Logger instance.
69
+ * @constructor
70
+ * @param {LoggerConfiguration?} [config] - Accepts configuration options @see {@link LoggerConfiguration}
71
+ */
42
72
  constructor(config?: LoggerConfiguration);
43
73
  /**
44
74
  * Log given message with level DEBUG. This will not be forwarded to centralized logging system!.
45
75
  *
46
76
  * @param message anything
47
- * @see log
77
+ * @see {@link LoggableType}
78
+ * @see {@link DtLogger.log}
48
79
  */
49
80
  debug(message: unknown): void;
50
81
  /**
51
82
  * Log given message with level INFO
52
83
  *
53
84
  * @param message Json-object to log
54
- * @see log
85
+ * @see {@link LoggableType}
86
+ * @see {@link DtLogger.log}
55
87
  */
56
88
  info(message: LoggableType): void;
57
89
  /**
58
90
  * Log given message with level WARN
59
91
  *
60
92
  * @param message Json-object to log
61
- * @see log
93
+ * @see {@link LoggableType}
94
+ * @see {@link DtLogger.log}
62
95
  */
63
96
  warn(message: LoggableType): void;
64
97
  /**
65
98
  * Log given message with level INFO
66
99
  *
67
100
  * @param message Json-object to log
68
- * @see log
101
+ * @see {@link LoggableType}
102
+ * @see {@link DtLogger.log}
69
103
  */
70
104
  error(message: LoggableType): void;
71
105
  /**
72
- * Log message with given log level. If message is a json object, it will be logged as it is and if it is a string it will be wrapped into json-element with key "message".
106
+ * Log message with given log level.
107
+ *
73
108
  * Some metadata is also added to the message:
74
109
  * * runtime - can be configured with constructor or inferred from environment
75
110
  * * lambdaName - can be configured with constructor or inferred from environment
76
- * * fileName - can be configured with constructor
77
111
  *
78
- * @param level "DEBUG", "INFO" or "ERROR"
79
- * @param message Either a string or json-object
112
+ * @param message Json-object to log
113
+ * @see {@link LoggableType}
80
114
  */
81
- log(level: LOG_LEVEL, message: LoggableType): void;
115
+ private log;
82
116
  }
83
- export {};
@@ -1,18 +1,28 @@
1
1
  "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
2
5
  Object.defineProperty(exports, "__esModule", { value: true });
3
6
  exports.DtLogger = void 0;
7
+ const lodash_1 = __importDefault(require("lodash"));
4
8
  /**
5
- * Helper class for json-logging. Logged line will include
9
+ * Helper class for json-logging.
10
+ *
11
+ * Logged line will include:
6
12
  * * log-level
7
13
  * * lambdaName (taken from process environment)
8
14
  * * runtime (taken from process environment)
9
15
  * * the actual message (as json or as string)
10
16
  */
11
17
  class DtLogger {
18
+ /**
19
+ * Create a new Logger instance.
20
+ * @constructor
21
+ * @param {LoggerConfiguration?} [config] - Accepts configuration options @see {@link LoggerConfiguration}
22
+ */
12
23
  constructor(config) {
13
24
  this.lambdaName =
14
25
  config?.lambdaName ?? process.env.AWS_LAMBDA_FUNCTION_NAME;
15
- this.fileName = config?.fileName;
16
26
  this.runtime = config?.runTime ?? process.env.AWS_EXECUTION_ENV;
17
27
  this.writeStream = config?.writeStream ?? process.stdout;
18
28
  }
@@ -20,13 +30,13 @@ class DtLogger {
20
30
  * Log given message with level DEBUG. This will not be forwarded to centralized logging system!.
21
31
  *
22
32
  * @param message anything
23
- * @see log
33
+ * @see {@link LoggableType}
34
+ * @see {@link DtLogger.log}
24
35
  */
25
36
  debug(message) {
26
37
  const logMessage = {
27
38
  message,
28
39
  level: "DEBUG",
29
- fileName: this.fileName,
30
40
  lambdaName: this.lambdaName,
31
41
  runtime: this.runtime,
32
42
  };
@@ -36,44 +46,54 @@ class DtLogger {
36
46
  * Log given message with level INFO
37
47
  *
38
48
  * @param message Json-object to log
39
- * @see log
49
+ * @see {@link LoggableType}
50
+ * @see {@link DtLogger.log}
40
51
  */
41
52
  info(message) {
42
- this.log("INFO", message);
53
+ this.log({ ...message, level: "INFO" });
43
54
  }
44
55
  /**
45
56
  * Log given message with level WARN
46
57
  *
47
58
  * @param message Json-object to log
48
- * @see log
59
+ * @see {@link LoggableType}
60
+ * @see {@link DtLogger.log}
49
61
  */
50
62
  warn(message) {
51
- this.log("WARN", message);
63
+ this.log({ ...message, level: "WARN" });
52
64
  }
53
65
  /**
54
66
  * Log given message with level INFO
55
67
  *
56
68
  * @param message Json-object to log
57
- * @see log
69
+ * @see {@link LoggableType}
70
+ * @see {@link DtLogger.log}
58
71
  */
59
72
  error(message) {
60
- this.log("ERROR", message);
73
+ this.log({
74
+ ...message,
75
+ level: "ERROR",
76
+ });
61
77
  }
62
78
  /**
63
- * Log message with given log level. If message is a json object, it will be logged as it is and if it is a string it will be wrapped into json-element with key "message".
79
+ * Log message with given log level.
80
+ *
64
81
  * Some metadata is also added to the message:
65
82
  * * runtime - can be configured with constructor or inferred from environment
66
83
  * * lambdaName - can be configured with constructor or inferred from environment
67
- * * fileName - can be configured with constructor
68
84
  *
69
- * @param level "DEBUG", "INFO" or "ERROR"
70
- * @param message Either a string or json-object
85
+ * @param message Json-object to log
86
+ * @see {@link LoggableType}
71
87
  */
72
- log(level, message) {
88
+ log(message) {
89
+ const error = message.error
90
+ ? typeof message.error === "string"
91
+ ? message.error
92
+ : JSON.stringify(message.error)
93
+ : undefined;
73
94
  const logMessage = {
74
- ...message,
75
- level,
76
- fileName: message.fileName ?? this.fileName,
95
+ ...removePrefix("custom", message),
96
+ error,
77
97
  lambdaName: this.lambdaName,
78
98
  runtime: this.runtime,
79
99
  };
@@ -81,4 +101,7 @@ class DtLogger {
81
101
  }
82
102
  }
83
103
  exports.DtLogger = DtLogger;
104
+ function removePrefix(prefix, loggable) {
105
+ return lodash_1.default.mapKeys(loggable, (_index, key) => key.startsWith(prefix) ? lodash_1.default.lowerFirst(key.replace(prefix, "")) : key);
106
+ }
84
107
  //# sourceMappingURL=dt-logger.js.map
@@ -11,6 +11,6 @@ export declare enum RdsSecretKey {
11
11
  host = "host",
12
12
  ro_host = "ro_host"
13
13
  }
14
- export declare type RdsProxySecret = Record<RdsProxySecretKey, string>;
15
- export declare type RdsSecret = Record<RdsSecretKey, string>;
14
+ export type RdsProxySecret = Record<RdsProxySecretKey, string>;
15
+ export type RdsSecret = Record<RdsSecretKey, string>;
16
16
  export declare function checkExpectedSecretKeys<Secret extends GenericSecret>(keys: string[], secret: Secret): void;
@@ -1,2 +1,2 @@
1
- export declare type GenericSecret = Record<string, string>;
1
+ export type GenericSecret = Record<string, string>;
2
2
  export declare function getSecret<Secret>(secretId: string, prefix?: string): Promise<Secret>;
@@ -6,8 +6,8 @@ export declare enum DatabaseEnvironmentKeys {
6
6
  DB_RO_URI = "DB_RO_URI",
7
7
  DB_APPLICATION = "DB_APPLICATION"
8
8
  }
9
- export declare type DTDatabase = IDatabase<unknown>;
10
- export declare type DTTransaction = ITask<unknown>;
9
+ export type DTDatabase = IDatabase<unknown>;
10
+ export type DTTransaction = ITask<unknown>;
11
11
  /**
12
12
  * Creates a non-pooling database connection primarily used by Lambdas.
13
13
  *
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * A simple asserter-class for writing canaries without dependency to testing-libraries.
3
3
  */
4
- declare type AssertedValue = string | number;
4
+ type AssertedValue = string | number;
5
5
  export declare abstract class Asserter {
6
6
  static assertEquals(value: AssertedValue, expected: AssertedValue): void;
7
7
  static assertTrue(value: boolean): void;
@@ -14,4 +14,4 @@ export declare class TestHttpServer {
14
14
  close(): void;
15
15
  private debuglog;
16
16
  }
17
- export declare type ListenProperties = Record<string, (url?: string, data?: string) => string>;
17
+ export type ListenProperties = Record<string, (url?: string, data?: string) => string>;
@@ -6,4 +6,4 @@ export interface EitherError {
6
6
  result: "error";
7
7
  message: string;
8
8
  }
9
- export declare type Either<T> = EitherOk<T> | EitherError;
9
+ export type Either<T> = EitherOk<T> | EitherError;
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Adds `null` as an accepted type to all properties in given type.
3
+ */
4
+ export type Nullable<Obj> = {
5
+ [Key in keyof Obj]: Obj[Key] | null;
6
+ };
7
+ type RequiredKeys<Obj> = {
8
+ [Key in keyof Obj]-?: object extends {
9
+ [K in Key]: Obj[Key];
10
+ } ? never : Key;
11
+ }[keyof Obj];
12
+ type OptionalKeys<Obj> = {
13
+ [Key in keyof Obj]-?: object extends {
14
+ [K in Key]: Obj[Key];
15
+ } ? Key : never;
16
+ }[keyof Obj];
17
+ type RequiredProperties<Obj> = Pick<Obj, RequiredKeys<Obj>>;
18
+ type OptionalProperties<Obj> = Pick<Obj, OptionalKeys<Obj>>;
19
+ /**
20
+ * Adds `null` as an accepted type to all optional properties in given type. Required properties remain unchanged.
21
+ */
22
+ export type NullableOptional<Obj> = RequiredProperties<Obj> & Nullable<OptionalProperties<Obj>>;
23
+ export {};
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=nullable.js.map
@@ -1 +1 @@
1
- export declare type URN<Namespace extends string, NamespaceSpecificString extends string = ""> = `urn:${Namespace}:${NamespaceSpecificString}`;
1
+ export type URN<Namespace extends string, NamespaceSpecificString extends string = ""> = `urn:${Namespace}:${NamespaceSpecificString}`;
@@ -1,4 +1,31 @@
1
- import { AxiosError } from "axios";
2
1
  import { DtLogger } from "../aws/runtime/dt-logger";
3
- export declare function logException(logger: DtLogger, error: Error | string, includeStack?: boolean): void;
4
- export declare function logException(logger: DtLogger, error: AxiosError): void;
2
+ /**
3
+ * Curried version of logException.
4
+ *
5
+ * @example <caption>Using default configuration</caption>
6
+ * Promise.reject(x).catch(createExceptionLogger())
7
+ *
8
+ * @example <caption>Providing external logger and requiring stack</caption>
9
+ * import {logger} from "@digitraffic/common/dist/aws/runtime/dt-logger-default"
10
+ * Promise.reject(x).catch(createExceptionLogger(logger, true))
11
+ *
12
+ * @param [logger=undefined] - DtLogger to use. If not given, will create a new instance of DtLogger
13
+ * @param [includeStack=false] - Define if the stack trace should be logged.
14
+ * @param error - The error instance to be logged.
15
+ * @returns Logs the error without rethrowing.
16
+ * @see {@link logException}
17
+ */
18
+ export declare function createExceptionLogger(logger?: DtLogger | undefined, includeStack?: boolean): (error: unknown) => void;
19
+ /**
20
+ * Log given exception with level ERROR to given logger.
21
+ *
22
+ * Supports AxiosError, Error and string
23
+ *
24
+ * @param logger - DtLogger to use
25
+ * @param error - AxiosError, Error or string to log
26
+ * @param [includeStack=true] - Include stack in the message, default false
27
+ * @returns Logs the error without rethrowing
28
+ * @see {@link DtLogger.log}
29
+ * @see {@link createExceptionLogger} for a curried setup
30
+ */
31
+ export declare function logException(logger: DtLogger, error: unknown, includeStack?: boolean): void;
@@ -1,28 +1,64 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.logException = void 0;
3
+ exports.logException = exports.createExceptionLogger = void 0;
4
4
  const axios_1 = require("axios");
5
+ const dt_logger_1 = require("../aws/runtime/dt-logger");
5
6
  const utils_1 = require("./utils");
6
7
  const functionName = (0, utils_1.getEnvVariableOrElse)("AWS_LAMBDA_FUNCTION_NAME", "test");
8
+ /**
9
+ * Curried version of logException.
10
+ *
11
+ * @example <caption>Using default configuration</caption>
12
+ * Promise.reject(x).catch(createExceptionLogger())
13
+ *
14
+ * @example <caption>Providing external logger and requiring stack</caption>
15
+ * import {logger} from "@digitraffic/common/dist/aws/runtime/dt-logger-default"
16
+ * Promise.reject(x).catch(createExceptionLogger(logger, true))
17
+ *
18
+ * @param [logger=undefined] - DtLogger to use. If not given, will create a new instance of DtLogger
19
+ * @param [includeStack=false] - Define if the stack trace should be logged.
20
+ * @param error - The error instance to be logged.
21
+ * @returns Logs the error without rethrowing.
22
+ * @see {@link logException}
23
+ */
24
+ function createExceptionLogger(logger = undefined, includeStack = false) {
25
+ let thatLogger;
26
+ if (logger) {
27
+ thatLogger = logger;
28
+ }
29
+ else {
30
+ thatLogger = new dt_logger_1.DtLogger();
31
+ }
32
+ return (error) => {
33
+ logException(thatLogger, error, includeStack);
34
+ };
35
+ }
36
+ exports.createExceptionLogger = createExceptionLogger;
7
37
  /**
8
38
  * Log given exception with level ERROR to given logger.
9
39
  *
10
40
  * Supports AxiosError, Error and string
11
41
  *
12
- * @param logger DtLogger to use
13
- * @param error AxiosError, Error or string to log
14
- * @param includeStack Include stack in the message, default false
15
- * @see log
42
+ * @param logger - DtLogger to use
43
+ * @param error - AxiosError, Error or string to log
44
+ * @param [includeStack=true] - Include stack in the message, default false
45
+ * @returns Logs the error without rethrowing
46
+ * @see {@link DtLogger.log}
47
+ * @see {@link createExceptionLogger} for a curried setup
16
48
  */
17
49
  function logException(logger, error, includeStack = false) {
18
- const message = error instanceof Error ? error.message : error;
50
+ const message = error instanceof Error
51
+ ? error.message
52
+ : typeof error === "string"
53
+ ? error
54
+ : JSON.stringify(error);
19
55
  const stack = error instanceof Error && includeStack ? error.stack : undefined;
20
- const code = error instanceof axios_1.AxiosError ? error.code : undefined;
56
+ const customCode = error instanceof axios_1.AxiosError ? error.code : undefined;
21
57
  logger.error({
22
58
  type: "Error",
23
59
  method: `${functionName}.logException`,
24
60
  message,
25
- code,
61
+ customCode,
26
62
  stack,
27
63
  });
28
64
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@digitraffic/common",
3
- "version": "2023.3.27-1",
3
+ "version": "2023.4.4-1",
4
4
  "description": "",
5
5
  "repository": {
6
6
  "type": "git",
@@ -35,19 +35,20 @@
35
35
  "@types/aws-lambda": "^8.10.114",
36
36
  "@types/geojson": "^7946.0.10",
37
37
  "@types/jest": "^29.5.0",
38
+ "@types/lodash": "^4.14.192",
38
39
  "@types/node": "^18.15.10",
39
40
  "@types/ramda": "^0.28.23",
40
41
  "@types/sinon": "^10.0.13",
41
- "@typescript-eslint/eslint-plugin": "^5.56.0",
42
+ "@typescript-eslint/eslint-plugin": "~5.56.0",
42
43
  "@typescript-eslint/parser": "^5.56.0",
43
- "aws-cdk-lib": "2.70.0",
44
+ "aws-cdk-lib": "^2.70.0",
44
45
  "aws-sdk": "^2.1343.0",
45
46
  "axios": "^1.3.4",
46
47
  "change-case": "^4.1.2",
47
48
  "constructs": "^10.1.292",
48
49
  "eslint": "~8.36.0",
49
50
  "eslint-config-prettier": "^8.8.0",
50
- "eslint-plugin-deprecation": "1.3.3",
51
+ "eslint-plugin-deprecation": "~1.3.3",
51
52
  "geojson-validation": "^1.0.2",
52
53
  "husky": "^8.0.3",
53
54
  "jest": "^29.5.0",
@@ -62,7 +63,7 @@
62
63
  "rimraf": "^4.1.0",
63
64
  "sinon": "^15.0.3",
64
65
  "ts-jest": "^29.0.5",
65
- "typescript": "~4.8.4"
66
+ "typescript": "~4.9.5"
66
67
  },
67
68
  "externals": [
68
69
  "aws-sdk",
@@ -71,12 +72,16 @@
71
72
  "lint-staged": {
72
73
  "*.{js,ts,css,md,yml,yaml,json}": "prettier --write"
73
74
  },
75
+ "dependencies": {
76
+ "lodash": "^4.17.21"
77
+ },
74
78
  "scripts": {
75
79
  "build": "tsc",
76
80
  "lint": "eslint --cache .",
77
81
  "eslint-report": "eslint . --format html",
78
82
  "ci:eslint-report": "eslint . --format html -o report.html",
79
83
  "clean": "rimraf dist output",
80
- "test": "jest --detectOpenHandles --forceExit --coverage --coverageDirectory=output/coverage/jest"
84
+ "test": "jest --detectOpenHandles --forceExit --coverage --coverageDirectory=output/coverage/jest",
85
+ "test:watch": "jest --detectOpenHandles --forceExit --coverage --coverageDirectory=output/coverage/jest --watch"
81
86
  }
82
87
  }
@@ -1,37 +1,76 @@
1
1
  import { Writable } from "stream";
2
+ import _ from "lodash";
2
3
 
3
- type LOG_LEVEL = "DEBUG" | "INFO" | "WARN" | "ERROR";
4
+ /** Logging level */
5
+ export type LOG_LEVEL = "DEBUG" | "INFO" | "WARN" | "ERROR";
4
6
 
7
+ /**
8
+ * Configuration object for configuring the Digitraffic logging utility
9
+ * @see {@link DtLogger}
10
+ */
5
11
  export interface LoggerConfiguration {
12
+ /** Name of the lambda */
6
13
  lambdaName?: string;
14
+ /** The file name where the logging occurs */
7
15
  fileName?: string;
16
+ /** The lambda runtime environment */
8
17
  runTime?: string;
18
+ /** Custom end point to write the logs to */
9
19
  writeStream?: Writable;
10
20
  }
11
21
 
12
- export interface LoggableType {
13
- /** name of method logging the message */
22
+ interface LoggableTypeInternal extends LoggableType {
23
+ level: LOG_LEVEL;
24
+ }
25
+
26
+ /**
27
+ * CustomParams allows to add keys prefixed with `custom` keyword to be added to an
28
+ * object.
29
+ */
30
+ export interface CustomParams {
31
+ /** do not log your apikey! */
32
+ customApikey?: never;
33
+ /** do not log your apikey! */
34
+ customApiKey?: never;
35
+ [key: `custom${Capitalize<string>}Count`]: number;
36
+
37
+ [key: `custom${Capitalize<string>}`]:
38
+ | string
39
+ | number
40
+ | boolean
41
+ | Date
42
+ | null
43
+ | undefined;
44
+ }
45
+
46
+ /**
47
+ * Digitraffic logging object.
48
+ *
49
+ * `method` property is the only required propetry. {@link CustomParams} can be added by
50
+ * prefixin the property with keyword `custom`. The prefix is removed before writing to
51
+ * logging end point.
52
+ *
53
+ * @see {@link CustomParams}
54
+ */
55
+ export interface LoggableType extends CustomParams {
56
+ /** Name of the method logging the message */
14
57
  method: `${string}.${string}`;
15
- /** message to log, optional */
58
+ /** Message to log, optional */
16
59
  message?: string;
17
- /** type of message, optional */
60
+ /** Type of message, optional */
18
61
  type?: string;
19
- /** stack trace, optional */
20
- stack?: string;
21
- /** amount of time some operation took in milliseconds, optional */
62
+ /** Stack trace, optional */
63
+ stack?: string | undefined;
64
+ /** Amount of time some operation took in milliseconds, optional */
22
65
  tookMs?: number;
23
- /** count of something, optional */
24
- count?: number;
25
- /** do not log your apikey! */
26
- apikey?: never;
27
- /** do not log your apikey! */
28
- apiKey?: never;
29
- /** any other loggable key */
30
- [key: string]: string | number | boolean | Date | undefined;
66
+ /** Pass error object, which will be stringified before logging */
67
+ error?: unknown;
31
68
  }
32
69
 
33
70
  /**
34
- * Helper class for json-logging. Logged line will include
71
+ * Helper class for json-logging.
72
+ *
73
+ * Logged line will include:
35
74
  * * log-level
36
75
  * * lambdaName (taken from process environment)
37
76
  * * runtime (taken from process environment)
@@ -39,15 +78,18 @@ export interface LoggableType {
39
78
  */
40
79
  export class DtLogger {
41
80
  readonly lambdaName?: string;
42
- readonly fileName?: string;
43
81
  readonly runtime?: string;
44
82
 
45
83
  readonly writeStream: Writable;
46
84
 
85
+ /**
86
+ * Create a new Logger instance.
87
+ * @constructor
88
+ * @param {LoggerConfiguration?} [config] - Accepts configuration options @see {@link LoggerConfiguration}
89
+ */
47
90
  constructor(config?: LoggerConfiguration) {
48
91
  this.lambdaName =
49
92
  config?.lambdaName ?? process.env.AWS_LAMBDA_FUNCTION_NAME;
50
- this.fileName = config?.fileName;
51
93
  this.runtime = config?.runTime ?? process.env.AWS_EXECUTION_ENV;
52
94
  this.writeStream = config?.writeStream ?? process.stdout;
53
95
  }
@@ -56,13 +98,13 @@ export class DtLogger {
56
98
  * Log given message with level DEBUG. This will not be forwarded to centralized logging system!.
57
99
  *
58
100
  * @param message anything
59
- * @see log
101
+ * @see {@link LoggableType}
102
+ * @see {@link DtLogger.log}
60
103
  */
61
104
  debug(message: unknown): void {
62
105
  const logMessage = {
63
106
  message,
64
107
  level: "DEBUG",
65
- fileName: this.fileName,
66
108
  lambdaName: this.lambdaName,
67
109
  runtime: this.runtime,
68
110
  };
@@ -74,46 +116,57 @@ export class DtLogger {
74
116
  * Log given message with level INFO
75
117
  *
76
118
  * @param message Json-object to log
77
- * @see log
119
+ * @see {@link LoggableType}
120
+ * @see {@link DtLogger.log}
78
121
  */
79
122
  info(message: LoggableType): void {
80
- this.log("INFO", message);
123
+ this.log({ ...message, level: "INFO" });
81
124
  }
82
125
 
83
126
  /**
84
127
  * Log given message with level WARN
85
128
  *
86
129
  * @param message Json-object to log
87
- * @see log
130
+ * @see {@link LoggableType}
131
+ * @see {@link DtLogger.log}
88
132
  */
89
133
  warn(message: LoggableType): void {
90
- this.log("WARN", message);
134
+ this.log({ ...message, level: "WARN" });
91
135
  }
92
136
  /**
93
137
  * Log given message with level INFO
94
138
  *
95
139
  * @param message Json-object to log
96
- * @see log
140
+ * @see {@link LoggableType}
141
+ * @see {@link DtLogger.log}
97
142
  */
98
143
  error(message: LoggableType): void {
99
- this.log("ERROR", message);
144
+ this.log({
145
+ ...message,
146
+ level: "ERROR",
147
+ });
100
148
  }
101
149
 
102
150
  /**
103
- * Log message with given log level. If message is a json object, it will be logged as it is and if it is a string it will be wrapped into json-element with key "message".
151
+ * Log message with given log level.
152
+ *
104
153
  * Some metadata is also added to the message:
105
154
  * * runtime - can be configured with constructor or inferred from environment
106
155
  * * lambdaName - can be configured with constructor or inferred from environment
107
- * * fileName - can be configured with constructor
108
156
  *
109
- * @param level "DEBUG", "INFO" or "ERROR"
110
- * @param message Either a string or json-object
157
+ * @param message Json-object to log
158
+ * @see {@link LoggableType}
111
159
  */
112
- log(level: LOG_LEVEL, message: LoggableType): void {
160
+ private log(message: LoggableTypeInternal): void {
161
+ const error = message.error
162
+ ? typeof message.error === "string"
163
+ ? message.error
164
+ : JSON.stringify(message.error)
165
+ : undefined;
166
+
113
167
  const logMessage = {
114
- ...message,
115
- level,
116
- fileName: message.fileName ?? this.fileName,
168
+ ...removePrefix("custom", message),
169
+ error,
117
170
  lambdaName: this.lambdaName,
118
171
  runtime: this.runtime,
119
172
  };
@@ -121,3 +174,9 @@ export class DtLogger {
121
174
  this.writeStream.write(JSON.stringify(logMessage) + "\n");
122
175
  }
123
176
  }
177
+
178
+ function removePrefix(prefix: string, loggable: LoggableType) {
179
+ return _.mapKeys(loggable, (_index, key: string) =>
180
+ key.startsWith(prefix) ? _.lowerFirst(key.replace(prefix, "")) : key
181
+ );
182
+ }
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Adds `null` as an accepted type to all properties in given type.
3
+ */
4
+ export type Nullable<Obj> = { [Key in keyof Obj]: Obj[Key] | null };
5
+
6
+ type RequiredKeys<Obj> = {
7
+ [Key in keyof Obj]-?: object extends { [K in Key]: Obj[Key] } ? never : Key;
8
+ }[keyof Obj];
9
+ type OptionalKeys<Obj> = {
10
+ [Key in keyof Obj]-?: object extends { [K in Key]: Obj[Key] } ? Key : never;
11
+ }[keyof Obj];
12
+ type RequiredProperties<Obj> = Pick<Obj, RequiredKeys<Obj>>;
13
+ type OptionalProperties<Obj> = Pick<Obj, OptionalKeys<Obj>>;
14
+
15
+ /**
16
+ * Adds `null` as an accepted type to all optional properties in given type. Required properties remain unchanged.
17
+ */
18
+ export type NullableOptional<Obj> = RequiredProperties<Obj> &
19
+ Nullable<OptionalProperties<Obj>>;
@@ -4,38 +4,72 @@ import { getEnvVariableOrElse } from "./utils";
4
4
 
5
5
  const functionName = getEnvVariableOrElse("AWS_LAMBDA_FUNCTION_NAME", "test");
6
6
 
7
- export function logException(
8
- logger: DtLogger,
9
- error: Error | string,
10
- includeStack?: boolean
11
- ): void;
12
- export function logException(logger: DtLogger, error: AxiosError): void;
7
+ /**
8
+ * Curried version of logException.
9
+ *
10
+ * @example <caption>Using default configuration</caption>
11
+ * Promise.reject(x).catch(createExceptionLogger())
12
+ *
13
+ * @example <caption>Providing external logger and requiring stack</caption>
14
+ * import {logger} from "@digitraffic/common/dist/aws/runtime/dt-logger-default"
15
+ * Promise.reject(x).catch(createExceptionLogger(logger, true))
16
+ *
17
+ * @param [logger=undefined] - DtLogger to use. If not given, will create a new instance of DtLogger
18
+ * @param [includeStack=false] - Define if the stack trace should be logged.
19
+ * @param error - The error instance to be logged.
20
+ * @returns Logs the error without rethrowing.
21
+ * @see {@link logException}
22
+ */
23
+ export function createExceptionLogger(
24
+ logger: DtLogger | undefined = undefined,
25
+ includeStack = false
26
+ ) {
27
+ let thatLogger: DtLogger;
28
+ if (logger) {
29
+ thatLogger = logger;
30
+ } else {
31
+ thatLogger = new DtLogger();
32
+ }
33
+
34
+ return (error: unknown) => {
35
+ logException(thatLogger, error, includeStack);
36
+ };
37
+ }
13
38
 
14
39
  /**
15
40
  * Log given exception with level ERROR to given logger.
16
41
  *
17
42
  * Supports AxiosError, Error and string
18
43
  *
19
- * @param logger DtLogger to use
20
- * @param error AxiosError, Error or string to log
21
- * @param includeStack Include stack in the message, default false
22
- * @see log
44
+ * @param logger - DtLogger to use
45
+ * @param error - AxiosError, Error or string to log
46
+ * @param [includeStack=true] - Include stack in the message, default false
47
+ * @returns Logs the error without rethrowing
48
+ * @see {@link DtLogger.log}
49
+ * @see {@link createExceptionLogger} for a curried setup
23
50
  */
24
51
  export function logException(
25
52
  logger: DtLogger,
26
- error: Error | string | AxiosError,
53
+ error: unknown,
27
54
  includeStack = false
28
55
  ) {
29
- const message = error instanceof Error ? error.message : error;
56
+ const message =
57
+ error instanceof Error
58
+ ? error.message
59
+ : typeof error === "string"
60
+ ? error
61
+ : JSON.stringify(error);
62
+
30
63
  const stack =
31
64
  error instanceof Error && includeStack ? error.stack : undefined;
32
- const code = error instanceof AxiosError ? error.code : undefined;
65
+
66
+ const customCode = error instanceof AxiosError ? error.code : undefined;
33
67
 
34
68
  logger.error({
35
69
  type: "Error",
36
70
  method: `${functionName}.logException`,
37
71
  message,
38
- code,
72
+ customCode,
39
73
  stack,
40
74
  });
41
75
  }