Skip to content

Commit

Permalink
Merge branch 'main' into fix-remove-features-002
Browse files Browse the repository at this point in the history
  • Loading branch information
jared-hunter authored Oct 16, 2023
2 parents 5313e81 + 5bef749 commit 139c6dd
Show file tree
Hide file tree
Showing 12 changed files with 191 additions and 3 deletions.
1 change: 1 addition & 0 deletions docs/docs/feature-library/00_overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ The **Flex Project Template** comes with a set of features enabled by default wi
| [Conference (external)](conference) | _provide agents the ability to conference in external numbers_ |
| [Conversation Transfer](conversation-transfer) | _introduce conversation-based messaging transfer functionality for agents_ |
| [Custom Transfer Directory](custom-transfer-directory) | _customize the agent and queue transfer directories_ |
| [Datadog Log Integration](datadog-log-integration) | _forward logs emitted by the template to datadog_
| [Device Manager](device-manager) | _provide agents the ability to select the audio output device_ |
| [Dispositions](dispositions) | _provide agents the ability to select a disposition/wrap-up code and enter notes_ |
| [Emoji Picker](emoji-picker) | _adds an emoji picker for messaging tasks_ |
Expand Down
37 changes: 37 additions & 0 deletions docs/docs/feature-library/datadog-log-integration.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
---
sidebar_label: datadog-log-integration
title: datadog-log-integration
---

This feature forwards logs emitted by the Flex Project Template to Datadog.

### Configuration

This feature allows the following configuration settings.

![datadog-log-integration settings](../../static/img/features/datadog-log-integration/settings.png)

| Setting | Description |
| --------| ------------|
| Api key | Your Datadog Account [client token](https://docs.datadoghq.com/account_management/api-app-keys/#client-tokens)
| Log Level | The minimum log level to send to Datadog. See [Logging](/flex-project-template/building/template-utilities/logging) for more details.
| Intake Region | The [Datadog Site](https://docs.datadoghq.com/getting_started/site/) for your account. Valid values are `us` | `us5` | `us3` | `eu`.
| Flush Timeout | In milliseconds. Because we send logs to datadog over HTTP - we do not want to make an HTTP request for every log written. This feature will buffer log messages for the flush timeout before making a single HTTP request to Datadog with all buffered log messages. If there are no logs within the Flush timeout, there is no HTTP request to Datadog. |

### Flex User Experience

Logs are forwarded automatically with no indication to the Flex User.

### Dependencies

You will need a Datadog account, with a [client token](https://docs.datadoghq.com/account_management/api-app-keys/#client-tokens), as well as your intake region, or [Datadog Site](https://docs.datadoghq.com/getting_started/site/).

_Client tokens are safe for use in browsers._

### Metadata

All logs sent to Datadog are decorated with common metadata. In this case, we add the worker name and workerSid to every log message sent to Datadog automatically!

### Note

Logs are forwarded to Datadog through HTTP, not the datadog sdk client. This is done to save build size of the template. If you're looking for a tighter integration with Datadog, consider a custom feature that implements their client sdk.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
7 changes: 7 additions & 0 deletions flex-config/ui_attributes.common.json
Original file line number Diff line number Diff line change
Expand Up @@ -351,6 +351,13 @@
},
"sip_support": {
"enabled": false
},
"datadog_log_integration": {
"enabled": false,
"log_level": "info",
"api_key": "",
"intake_region": "",
"flush_timeout": 5000
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion plugin-flex-ts-template-v2/.prettierignore
Original file line number Diff line number Diff line change
@@ -1 +1 @@
README.md
README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { getFeatureFlags } from '../../utils/configuration';
import DatadogLogIntegrationConfig from './types/ServiceConfiguration';

const {
enabled = false,
api_key,
intake_region,
flush_timeout,
log_level,
} = (getFeatureFlags()?.features?.datadog_log_integration as DatadogLogIntegrationConfig) || {};

export const isFeatureEnabled = () => {
return enabled;
};

export const getApiKey = () => {
return api_key;
};

export const getIntakeRegion = () => {
return intake_region;
};

export const getFlushTimeout = () => {
return flush_timeout;
};

export const getLogLevel = () => {
return log_level;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import querystring from 'querystring';

import Destination from '../../../utils/logger/destination';
import { LogLevel } from '../../../utils/logger';
import { DatadogDestinationConfig } from '../types/ServiceConfiguration';

export default class DatadogDestination extends Destination {
buffer: any[] = [];

api: string = '';

constructor(opts: DatadogDestinationConfig) {
super({ minLogLevel: opts.minLogLevel });

const { flushTimeout, apiKey, intakeRegion } = opts;

if (intakeRegion === 'eu') {
this.api = `https://http-intake.logs.datadoghq.eu/api/v2/logs`;
} else if (intakeRegion === 'us3') {
this.api = `https://http-intake.logs.us3.datadoghq.com/api/v2/logs`;
} else if (intakeRegion === 'us5') {
this.api = `https://http-intake.logs.us5.datadoghq.com/api/v2/logs`;
} else {
this.api = `https://http-intake.logs.datadoghq.com/api/v2/logs`;
}

const query = {
'dd-api-key': apiKey,
};
const qs = querystring.encode(query);

this.api = `${this.api}?${qs}`;

setInterval(() => {
this.flush();
}, flushTimeout);
}

async handle(level: LogLevel, message: string, context: any, meta: any): Promise<void> {
return new Promise((resolve) => {
this.buffer.push({
level,
message,
context,
meta,
});
return resolve();
});
}

hasUnsentLogs(): boolean {
return Boolean(this.buffer.length);
}

async flush(): Promise<void> {
if (!this.hasUnsentLogs()) {
return;
}

try {
await fetch(this.api, {
method: 'POST',
headers: {
'content-type': 'application/json',
},
body: JSON.stringify(this.buffer),
});
} catch (err) {
console.error(err);
} finally {
// reset the buffer
this.buffer = [];
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import Datadog from '../../destination/Datadog';
import { getLogLevel, getApiKey, getIntakeRegion, getFlushTimeout } from '../../config';

export const loggerHook = function sendLogsToDataDog() {
return new Datadog({
apiKey: getApiKey(),
intakeRegion: getIntakeRegion(),
flushTimeout: getFlushTimeout(),
minLogLevel: getLogLevel(),
});
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { FeatureDefinition } from '../../types/feature-loader';
import { isFeatureEnabled } from './config';
// @ts-ignore
import hooks from './flex-hooks/**/*.*';

export const register = (): FeatureDefinition => {
if (!isFeatureEnabled()) return {};
return { name: 'datadog-log-integration', hooks: typeof hooks === 'undefined' ? [] : hooks };
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { LogLevel } from 'utils/logger';

export default interface DatadogLogIntegrationConfig {
enabled: boolean;
log_level: LogLevel;
api_key: string;
intake_region: string;
flush_timeout: number | undefined;
}

export interface DatadogDestinationConfig {
minLogLevel: LogLevel;
apiKey: string;
intakeRegion: string;
flushTimeout: number | undefined;
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React, { useState, useEffect } from 'react';
import { Actions, ITask } from '@twilio/flex-ui';
import { Flex, Tabs, TabList, Tab, TabPanels, TabPanel } from '@twilio-paste/core';
import { Flex } from '@twilio-paste/core/flex';
import { Tabs, TabList, Tab, TabPanels, TabPanel } from '@twilio-paste/core/tabs';

export interface Props {
thisTask?: ITask; // task assigned to component
Expand Down
3 changes: 2 additions & 1 deletion plugin-flex-ts-template-v2/src/utils/feature-loader/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ import * as TaskRouterReplaceCompleteTask from '../serverless/TaskRouter/Complet
import * as Logger from './logger';
import * as SendLogsToBrowserConsole from '../logger/sendLogsToBrowserConsole';
// @ts-ignore
import features from '../../feature-library/*';
// eslint-disable-next-line import/no-useless-path-segments
import features from '../../feature-library/*/index.ts';

export const initFeatures = (flex: typeof Flex, manager: Flex.Manager) => {
if (typeof features === 'undefined') {
Expand Down

0 comments on commit 139c6dd

Please sign in to comment.