Repository: awslabs/aws-embedded-metrics-node (ts)

Issue:
Better support for FireLens
- Add a FireLens environment that checks for `FLUENT_HOST` and uses that to configure the sink
- FireLens does not need the LogGroup/LogStream fields in the metadata object since it is configured on the agent itself
https://github.com/awslabs/aws-embedded-metrics-node/blob/6132122fcf550eeafee86d1c3f2167fbd65ca08f/src/sinks/AgentSink.ts#L92-L95

We _could_ do this by subclassing AgentSink into `GenericAgentSink` and `CloudWatchAgentSink` or by delegating configuration of the `AgentSink` to the environment.

Alternatively, we could just make LogGroup entirely optional for agents.


Pull request description:
#17 generic agent support
Closes #17 

Some agents (such as Fluent-Bit) handle LogGroup configuration in the agent directly. By allowing the LogGroup to be optional, we provide better support for these scenarios without including some arbitrary and incorrect LogGroup name in the payload

Patch:
diff --git a/src/environment/DefaultEnvironment.ts b/src/environment/DefaultEnvironment.ts
index 2e39776..5c97d09 100644
--- a/src/environment/DefaultEnvironment.ts
+++ b/src/environment/DefaultEnvironment.ts
@@ -43,6 +43,12 @@ export class DefaultEnvironment implements IEnvironment {
   }
 
   public getLogGroupName(): string {
+    // if the caller explicitly overrides logGroupName to 
+    // be empty, we should honor that rather than providing 
+    // the default behavior.
+    if (config.logGroupName === "") {
+      return "";
+    }
     return config.logGroupName ? config.logGroupName : `${this.getName()}-metrics`;
   }
 
diff --git a/src/sinks/AgentSink.ts b/src/sinks/AgentSink.ts
index 17ef032..06bcfbc 100644
--- a/src/sinks/AgentSink.ts
+++ b/src/sinks/AgentSink.ts
@@ -89,7 +89,10 @@ export class AgentSink implements ISink {
   }
 
   public async accept(context: MetricsContext): Promise<void> {
-    context.meta.LogGroupName = this.logGroupName;
+    if (this.logGroupName) {
+      context.meta.LogGroupName = this.logGroupName;
+    }
+   
     if (this.logStreamName) {
       context.meta.LogStreamName = this.logStreamName;
     }


Test patch:
diff --git a/src/environment/__tests__/DefaultEnvironment.test.ts b/src/environment/__tests__/DefaultEnvironment.test.ts
index f3717c1..3cba88c 100644
--- a/src/environment/__tests__/DefaultEnvironment.test.ts
+++ b/src/environment/__tests__/DefaultEnvironment.test.ts
@@ -88,6 +88,18 @@ test('getLogGroupName() returns <ServiceName>-metrics if not configured', () =>
   expect(result).toBe(`${serviceName}-metrics`);
 });
 
+test('getLogGroupName() returns empty if explicitly set to empty', () => {
+  // arrange
+  config.logGroupName = "";
+  const env = new DefaultEnvironment();
+
+  // act
+  const result = env.getLogGroupName();
+
+  // assert
+  expect(result).toBe(``);
+});
+
 test('getSink() creates an AgentSink', () => {
   // arrange
   const expectedSink = 'AgentSink';
diff --git a/src/sinks/__tests__/AgentSink.test.ts b/src/sinks/__tests__/AgentSink.test.ts
index 3c46949..b1a4cc9 100644
--- a/src/sinks/__tests__/AgentSink.test.ts
+++ b/src/sinks/__tests__/AgentSink.test.ts
@@ -2,6 +2,8 @@ import * as faker from 'faker';
 import Configuration from '../../config/Configuration';
 import { MetricsContext } from '../../logger/MetricsContext';
 import { AgentSink } from '../AgentSink';
+import { TcpClient } from '../connections/TcpClient';
+
 
 test('default endpoint is tcp', () => {
   // arrange
@@ -47,3 +49,42 @@ test('handles tcp connection error', async () => {
   // assert
   return expect(sink.accept(context)).rejects.toThrowError(/ECONNREFUSED/);
 });
+
+test('LogGroup is stored in metadata', async () => {
+  // arrange
+  const expectedLogGroup = faker.random.alphaNumeric();
+  const context = MetricsContext.empty();
+
+  let receivedMessage: any = {};
+  TcpClient.prototype.sendMessage = (message: Buffer) : Promise<void> => {
+    receivedMessage = JSON.parse(message.toString('utf8'));
+    return Promise.resolve();
+  }
+
+  const sink = new AgentSink(expectedLogGroup);
+
+  // act
+  await sink.accept(context);
+
+  // assert
+  expect(receivedMessage._aws.LogGroupName).toBe(expectedLogGroup);
+});
+
+test('empty LogGroup is not stored in metadata', async () => {
+  // arrange
+  const context = MetricsContext.empty();
+
+  let receivedMessage: any = {};
+  TcpClient.prototype.sendMessage = (message: Buffer) : Promise<void> => {
+    receivedMessage = JSON.parse(message.toString('utf8'));
+    return Promise.resolve();
+  }
+
+  const sink = new AgentSink("");
+
+  // act
+  await sink.accept(context);
+
+  // assert
+  expect(receivedMessage._aws.LogGroupName).toBe(undefined);
+});