Skip to content

Commit

Permalink
Add New Relic transport
Browse files Browse the repository at this point in the history
  • Loading branch information
Hexagon committed Mar 18, 2024
1 parent 145788f commit e2a9f53
Show file tree
Hide file tree
Showing 5 changed files with 146 additions and 15 deletions.
65 changes: 53 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
**@cross/log - Generic logger for Node.js, Deno, and Bun**
**@cross/log - Flexible cross-runtime logging for Node.js, Deno, and Bun**

## Installation

Expand All @@ -7,14 +7,9 @@ Instructions are available at
version:

```bash
# Deno
deno add @cross/log

# Node.js
npx jsr add @cross/log

# Bun
bunx jsr add @cross/log
deno add @cross/log # Deno
npx jsr add @cross/log # Node.js
bunx jsr add @cross/log # Bun
```

## Getting Started
Expand All @@ -38,7 +33,7 @@ logger.info("Hello log!");
- **Cross-Runtime Compatibility** Works seamlessly across Node.js, Deno, and Bun
environments.
- **Modular Transports:** Easily create custom transports to send logs to
databases, network services, or specialized systems.
console, file or cloud services like Splunk HEC or New Relic.
- **Global and Transport Filtering:** Set log levels globally or configure
fine-grained filtering per transport.

Expand Down Expand Up @@ -102,11 +97,57 @@ try {
}
```

#### New Relic transport

**Configuration**

The `NewRelicLogger` transport requires the following options:

- **`apiKey`:** Your New Relic license key or API key.
- **`region`:** (Optional) The New Relic region where your account is located.
Valid values are "US", "EU", and "FedRamp". Defaults to "US".
- **`serviceAttribute`:** (Optional) A common attribute name to identify the
service generating the logs.
- **`logtypeAttribute`:** (Optional) A common attribute name to categorize your
log entries.
- **`hostnameAttribute`:** (Optional) A common attribute name to indicate the
host where the logs originated.

**Example**

```javascript
import { Log, NewRelicLogger } from "@cross/log";

const logger = new Log([
// ... other transports (ConsoleLogger, FileLogger, etc.)

new NewRelicLogger({
apiKey: "your-new-relic-api-key",
region: "EU",
serviceAttribute: "my-awesome-app",
logtypeAttribute: "application-logs",
hostnameAttribute: "production-server-1",
}),
]);

logger.warn("User successfully logged in");
```

**Important Notes**

- **Obtain your API Key:** You can find your New Relic license key or API key
within your New Relic account settings.
- **Custom Attributes:** The `serviceAttribute`, `logtypeAttribute` and
`hostnameAttribute` help you organize and filter your logs within New Relic.
- **Error Handling:** Consider robust error handling for your New Relic
transport (e.g., storing logs temporarily in a file if the New Relic API is
unavailable).

#### Creating a custom transport

```ts
import { Log, LogTransportBase, LogTransportBaseOptions } from "./mod.ts";
import { Severity } from "./mod.ts";
import { Log, LogTransportBase, LogTransportBaseOptions } from "@cross/log";
import { Severity } from "@cross/log";

/**
* Create your own Transport by extending LogTransportBase
Expand Down
2 changes: 1 addition & 1 deletion deno.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@cross/log",
"version": "0.10.1",
"version": "0.10.2",
"exports": {
".": "./mod.ts",
"./console": "./transports/console.ts",
Expand Down
2 changes: 2 additions & 0 deletions mod.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ export { Severity } from "./src/types.ts";
*/
export { ConsoleLogger } from "./transports/console.ts";
export { FileLogger } from "./transports/file.ts";
export { SplunkHecLogger } from "./transports/splunk.ts";
export { NewRelicLogger } from "./transports/newrelic.ts";

/**
* Re-export of transport classes
Expand Down
88 changes: 88 additions & 0 deletions transports/newrelic.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import {
LogTransport,
LogTransportBase,
LogTransportBaseOptions,
} from "./base.ts";
import { Severity } from "../src/types.ts";
import { deepMerge } from "@cross/deepmerge";

interface NewRelicLoggerOptions extends LogTransportBaseOptions {
apiKey: string; // The user's New Relic API key
region?: string; // The New Relic region (e.g., 'US', 'EU')
serviceAttribute?: string;
logtypeAttribute?: string;
hostnameAttribute?: string;
}

export class NewRelicLogger extends LogTransportBase implements LogTransport {
options: NewRelicLoggerOptions;

constructor(options: NewRelicLoggerOptions) {
super();
this.options = deepMerge(
this.defaults as NewRelicLoggerOptions,
options,
)!;
}

log(severity: Severity, scope: string, data: unknown[], timestamp: Date) {
if (this.shouldLog(severity)) {
const event = this.formatEvent(severity, scope, data, timestamp);
this.sendToNewRelic(event);
}
}

private formatEvent(
severity: Severity,
scope: string,
data: unknown[],
timestamp: Date,
): object {
return {
logtype: this.options.logtypeAttribute,
service: this.options.serviceAttribute,
hostname: this.options.hostnameAttribute,
timestamp: timestamp.getTime(),
message: data.join(" "),
severity,
scope,
};
}

private async sendToNewRelic(event: object) {
const endpoint = this.getEndpoint();
try {
const response = await fetch(endpoint, {
method: "POST",
body: JSON.stringify(event),
headers: {
"Content-Type": "application/json",
"Api-Key": this.options.apiKey,
},
});
if (!response.ok) {
console.error(
"Error sending log event to New Relic: ",
response.status,
response.statusText,
);
}
} catch (error) {
console.error("Network error sending log event to New Relic:", error);
}
}

private getEndpoint(): string {
if (this.options.region === "US") {
return "https://log-api.newrelic.com/log/v1";
} else if (this.options.region === "EU") {
return "https://log-api.eu.newrelic.com/log/v1";
} else if (this.options.region === "FedRamp") {
return "https://gov-log-api.newrelic.com/log/v1";
} else {
throw new Error(
"Unknown New Relic Region. Please check your configuration.",
);
}
}
}
4 changes: 2 additions & 2 deletions transports/splunk.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import {
LogTransport,
LogTransportBase,
LogTransportBaseOptions,
} from "../src/transport.ts";
} from "./base.ts";
import { Severity } from "../src/types.ts";
import { deepMerge } from "@cross/deepmerge";

Expand All @@ -14,7 +14,7 @@ interface SplunkHecClientOptions extends LogTransportBaseOptions {
sourceType?: string;
}

export class SplunkHecClient extends LogTransportBase implements LogTransport {
export class SplunkHecLogger extends LogTransportBase implements LogTransport {
options: SplunkHecClientOptions;
constructor(options: SplunkHecClientOptions) {
super();
Expand Down

0 comments on commit e2a9f53

Please sign in to comment.