Skip to content

Commit

Permalink
Merge branch 'pr/bobheadxi/54'
Browse files Browse the repository at this point in the history
  • Loading branch information
ovhemert committed Apr 23, 2020
2 parents e09f0ec + 776b6e2 commit 2bd0615
Show file tree
Hide file tree
Showing 8 changed files with 120 additions and 11 deletions.
9 changes: 8 additions & 1 deletion docs/API.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ const writeStream = stackdriver.createWriteStream({
credentials: '/credentials.json',
projectId: 'my-project'
})
````
```

#### credentials

Expand All @@ -56,3 +56,10 @@ The name of the log. Defaults to `"pino_log"`.
Type: `{ type: String, labels: Object }` *(optional)*

The resource to send logs to. Defaults to `{ type: "global" }`.

#### keys

Type: `{ [key]: key }` *(optional)*

Customize additional fields to pull from log messages and include in meta. Currently
supports `httpRequest`, `trace`. Defaults to `{ httpRequest: "httpRequest" }`.
1 change: 1 addition & 0 deletions docs/CLI.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ You can pass the following options via cli arguments:
| -V | --version | Output the version number |
| -p | --project <project> | Your Google Cloud Platform project ID (or use env var PROJECT_ID) |
| -c | --credentials <credentials> | The file path of the JSON file that contains your service account key (or use env var GOOGLE_APPLICATION_CREDENTIALS) |
| -k | --key <key:customKey> | Repeatable `key:customKey` pairs for custom keys (see [API docs](./CLI.md#keys))
| -h | --help | Output usage information |

See the [API](./API.md) documentation for details.
11 changes: 10 additions & 1 deletion pino-stackdriver.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,16 @@ declare namespace PinoStackdriver {
resource?: {
type: string;
labels?: Record<string, string>;
}
};

/**
* Names of log fields to pull properties out of - see https://cloud.google.com/logging/docs/reference/v2/rest/v2/LogEntry
* @default { httpRequest: "httpRequest", trace: "trace", ... }
*/
keys?: {
httpRequest?: string;
trace?: string;
};
}

/**
Expand Down
17 changes: 15 additions & 2 deletions src/cli.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,31 @@ const program = require('commander')
const pkg = require('../package.json')
const stackdriver = require('././index')

function collect (value, previous) {
return previous.concat([value])
}

// main cli logic
function main () {
program
.version(pkg.version)
.option('-c, --credentials <credentials>', 'The file path of the JSON file that contains your service account key')
.option('-p, --project <project>', 'Your Google Cloud Platform project ID')
.action(({ credentials, project }) => {
.option('-k, --key <key:customKey>', 'Customize additional data to include in log metadata', collect, [])
.action(({ credentials, project, key }) => {
try {
const _credentials = credentials || process.env.GOOGLE_APPLICATION_CREDENTIALS
if (!process.env.PROJECT_ID && !project) { throw Error('Project is missing.') }
const _project = project || process.env.PROJECT_ID
const writeStream = stackdriver.createWriteStream({ credentials: _credentials, projectId: _project })

const customKeys = {}
key.forEach(k => {
const pair = k.split(':')
if (pair.length !== 2) { throw Error(`Invalid key:customKey pair ${k}`) }
customKeys[pair[0]] = pair[1]
})

const writeStream = stackdriver.createWriteStream({ credentials: _credentials, projectId: _project, keys: customKeys })
process.stdin.pipe(writeStream)
console.info('logging')
} catch (error) {
Expand Down
10 changes: 8 additions & 2 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
const stackdriver = require('./stackdriver')
const pumpify = require('pumpify')

module.exports.createWriteStream = ({ credentials, logName, projectId, resource }) => {
module.exports.createWriteStream = ({
credentials,
logName,
projectId,
resource,
keys
}) => {
const parseJsonStream = stackdriver.parseJsonStream()
const toLogEntryStream = stackdriver.toLogEntryStream({ resource })
const toLogEntryStream = stackdriver.toLogEntryStream({ resource, keys })
const toStackdriverStream = stackdriver.toStackdriverStream({ credentials, logName, projectId })
return pumpify(parseJsonStream, toLogEntryStream, toStackdriverStream)
}
27 changes: 23 additions & 4 deletions src/stackdriver.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,31 @@ function _levelToSeverity (level) {
return 'default'
}

const defaultKeys = {
httpRequest: 'httpRequest',
trace: undefined
}

function _getKey (log, data, k, keys) {
// use custom key, otherwise use default keys
const key = (keys && keys[k]) ? keys[k] : defaultKeys[k]
// if no key is specified, return nothing
if (!key) return undefined
// if a value is defined, remove it from the log message to avoid double-loggin
const v = log[key]
if (v) {
delete data[key]
return v
}
return undefined
}

module.exports.parseJsonStream = function () {
return split2(_jsonParser)
}

module.exports.toLogEntry = function (log, options = {}) {
const { labels, prefix, resource } = options
const { labels, prefix, resource, keys } = options

const severity = _levelToSeverity(log.level)
let message = log.msg || log.message || severity
Expand All @@ -38,16 +57,16 @@ module.exports.toLogEntry = function (log, options = {}) {
const data = { ...log, message }
if (data.msg) { delete data.msg }
if (data.labels) { delete data.labels }
if (data.httpRequest) { delete data.httpRequest }

const entry = {
meta: {
resource: resource || { type: 'global' },
severity
severity,
trace: _getKey(log, data, 'trace', keys),
httpRequest: _getKey(log, data, 'httpRequest', keys)
},
data
}
if (log.httpRequest) { entry.meta.httpRequest = log.httpRequest }
if (labels || log.labels) {
const optLabels = labels || {}
const logLabels = log.labels || {}
Expand Down
23 changes: 23 additions & 0 deletions test/cli.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,29 @@ test('throws on missing project', (t) => {
})
})

test('parses custom keys', (t) => {
t.plan(1)
delete process.env.PROJECT_ID
const app = spawn('node', [appPath, '-p', 'project-id', '--key', 'httpRequest:req', '--key', 'trace:trace'])
app.stdout.on('data', (data) => {
const msg = data.toString()
const res = (msg.indexOf('logging') >= 0)
t.ok(res)
app.kill()
})
})

test('throws on invalid key', (t) => {
t.plan(1)
delete process.env.PROJECT_ID
const app = spawn('node', [appPath, '-p', 'project-id', '--key', 'httpRequest'])
app.stdout.on('data', (data) => {
const msg = data.toString()
const res = (msg.indexOf('Invalid key:customKey pair') >= 0)
t.ok(res)
})
})

test('picks up environment variables', (t) => {
t.plan(1)
process.env.GOOGLE_APPLICATION_CREDENTIALS = './credentials.json'
Expand Down
33 changes: 32 additions & 1 deletion test/stackdriver.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -88,11 +88,42 @@ test('adds labels to log entry message', t => {
})

test('adds httpRequest to log entry message', t => {
t.plan(3)

const log = { level: 30, time: parseInt('1532081790730', 10), httpRequest: { url: 'http://localhost/' }, trace: 'my/trace/id', pid: 9118, hostname: 'Osmonds-MacBook-Pro.local', v: 1 }
const entry = tested.toLogEntry(log)
t.ok(entry.meta.severity === 'info')
t.ok(entry.meta.httpRequest.url === 'http://localhost/')

// by default, do not include trace
t.ok(entry.meta.trace === undefined)
})

test('adds httpRequest with custom key to log entry message', t => {
t.plan(2)

const log = { level: 30, time: parseInt('1532081790730', 10), httpRequest: { url: 'http://localhost/' }, pid: 9118, hostname: 'Osmonds-MacBook-Pro.local', v: 1 }
const log = { level: 30, time: parseInt('1532081790730', 10), req: { url: 'http://localhost/' }, pid: 9118, hostname: 'Osmonds-MacBook-Pro.local', v: 1 }
const entry = tested.toLogEntry(log, { keys: { httpRequest: 'req' } })
t.ok(entry.meta.severity === 'info')
t.ok(entry.meta.httpRequest.url === 'http://localhost/')
})

test('does not add trace to log entry message by default', t => {
t.plan(2)

const log = { level: 30, time: parseInt('1532081790730', 10), trace: 'my/trace/id', pid: 9118, hostname: 'Osmonds-MacBook-Pro.local', v: 1 }
const entry = tested.toLogEntry(log)
t.ok(entry.meta.severity === 'info')
t.ok(entry.meta.trace === undefined)
})

test('adds trace to log entry message with option', t => {
t.plan(3)

const log = { level: 30, time: parseInt('1532081790730', 10), trace: 'my/trace/id', httpRequest: { url: 'http://localhost/' }, pid: 9118, hostname: 'Osmonds-MacBook-Pro.local', v: 1 }
const entry = tested.toLogEntry(log, { keys: { trace: 'trace' } })
t.ok(entry.meta.severity === 'info')
t.ok(entry.meta.trace === 'my/trace/id')
t.ok(entry.meta.httpRequest.url === 'http://localhost/')
})

Expand Down

0 comments on commit 2bd0615

Please sign in to comment.