Skip to content

Commit

Permalink
feat: custom logging (#1205)
Browse files Browse the repository at this point in the history
  • Loading branch information
gr2m committed Jan 31, 2019
1 parent eac7427 commit cbb5c41
Show file tree
Hide file tree
Showing 11 changed files with 336 additions and 55 deletions.
70 changes: 66 additions & 4 deletions README.md
Expand Up @@ -23,6 +23,7 @@
- [Register custom endpoint methods](#register-custom-endpoint-methods)
- [Throttling](#throttling)
- [Automatic retries](#automatic-retries)
- [Logging](#logging)
- [Debug](#debug)
- [Contributing](#contributing)
- [Credits](#credits)
Expand Down Expand Up @@ -94,6 +95,14 @@ const octokit = new Octokit({

// set custom URL for on-premise GitHub Enterprise installations
baseUrl: 'https://api.github.com',

// pass custom methods for debug, info, warn and error
log: {
debug: () => {},
info: () => {},
warn: console.warn,
error: console.error
},

request: {
// Node.js only: advanced request options can be passed as http(s) agent,
Expand Down Expand Up @@ -360,14 +369,16 @@ module.exports = (octokit, options = { greeting: 'Hello' }) => {
octokit.hook.wrap('request', async (request, options) => {
const time = Date.now()
const response = await request(options)
console.log(`${options.method} ${options.url}${response.status} in ${Date.now() - time}ms`)
octokit.log.info(`${options.method} ${options.url}${response.status} in ${Date.now() - time}ms`)
return response
})
}
```

`.plugin` accepts a function or an array of functions.

We recommend using [Octokit’s log methods](#logging) to help users of your plugin with debugging.

You can add new methods to the `octokit` instance passed as the first argument to
the plugin function. The 2nd argument is the options object passed to the
constructor when instantiating the `octokit` client.
Expand Down Expand Up @@ -441,7 +452,7 @@ const octokit = new Octokit({
auth: 'token ' + process.env.TOKEN,
throttle: {
onRateLimit: (retryAfter, options) => {
console.warn(`Request quota exhausted for request ${options.method} ${options.url}`)
octokit.log.warn(`Request quota exhausted for request ${options.method} ${options.url}`)

if (options.request.retryCount === 0) { // only retries once
console.log(`Retrying after ${retryAfter} seconds!`)
Expand All @@ -450,7 +461,7 @@ const octokit = new Octokit({
},
onAbuseLimit: (retryAfter, options) => {
// does not retry, only logs a warning
console.warn(`Abuse detected for request ${options.method} ${options.url}`)
octokit.log.warn(`Abuse detected for request ${options.method} ${options.url}`)
}
}
})
Expand All @@ -469,9 +480,60 @@ const octokit = new Octokit()
// all requests sent with the `octokit` instance are now retried up to 3 times for recoverable errors.
```

## Logging

`Octokit` has 4 built in log methods

1. `octokit.log.debug(message[, additionalInfo])`
1. `octokit.log.info(message[, additionalInfo])`
1. `octokit.log.warn(message[, additionalInfo])`
1. `octokit.log.error(message[, additionalInfo])`

They can be configured using the [`log` client option](client-options). By default, `octokit.log.debug()` and `octokit.log.info()` are no-ops, while the other two call `console.warn()` and `console.error()` respectively.

This is useful if you build reusable [plugins](#plugins).

## Debug

Set `DEBUG=octokit:rest*` for additional debug logs.
The simplest way to receive debug information is to set the [`log` client option](client-options) to `console`.

```js
const octokit = require('@octokit/rest')({
log: console
})

console.request('/')
```

This will log

```
request { method: 'GET',
baseUrl: 'https://api.github.com',
headers:
{ accept: 'application/vnd.github.v3+json',
'user-agent':
'octokit.js/0.0.0-semantically-released Node.js/10.15.0 (macOS Mojave; x64)' },
request: {},
url: '/' }
GET / - 200 in 514ms
```

If you like to support a configurable log level, we recommend using the [console-log-level](https://github.com/watson/console-log-level) module

```js
const octokit = require('@octokit/rest')({
log: require('console-log-level')({ level: 'info' })
})

console.request('/')
```

This will only log

```
GET / - 200 in 514ms
```

## Contributing

Expand Down
1 change: 1 addition & 0 deletions index.js
@@ -1,6 +1,7 @@
const Octokit = require('./lib/core')

const CORE_PLUGINS = [
require('./plugins/log'),
require('./plugins/authentication-deprecated'), // deprecated: remove in v17
require('./plugins/authentication'),
require('./plugins/pagination'),
Expand Down
9 changes: 8 additions & 1 deletion lib/constructor.js
Expand Up @@ -9,9 +9,16 @@ const requestWithDefaults = require('./request-with-defaults')
function Octokit (plugins, options) {
options = options || {}
const hook = new Hook()
const log = Object.assign({
'debug': () => {},
'info': () => {},
'warn': console.warn,
'error': console.error
}, options && options.log)
const api = {
hook,
request: requestWithDefaults(hook, endpoint, parseClientOptions(options))
log,
request: requestWithDefaults(hook, endpoint, parseClientOptions(options, log))
}

plugins.forEach(pluginFunction => pluginFunction(api, options))
Expand Down
8 changes: 4 additions & 4 deletions lib/parse-client-options.js
Expand Up @@ -4,7 +4,7 @@ const getUserAgent = require('universal-user-agent')

const pkg = require('../package.json')

function parseOptions (options) {
function parseOptions (options, log) {
if (options.headers) {
options.headers = Object.keys(options.headers).reduce((newObj, key) => {
newObj[key.toLowerCase()] = options.headers[key]
Expand All @@ -30,17 +30,17 @@ function parseOptions (options) {
}

if (options.timeout) {
console.warn(new Error('new Octokit({timout}) is deprecated. Use {request: {agent}} instead. See https://github.com/octokit/rest.js#client-options'))
log.warn(new Error('new Octokit({timout}) is deprecated. Use {request: {agent}} instead. See https://github.com/octokit/rest.js#client-options'))
clientDefaults.request.timeout = options.timeout
}

if (options.agent) {
console.warn(new Error('new Octokit({agent}) is deprecated. Use {request: {agent}} instead. See https://github.com/octokit/rest.js#client-options'))
log.warn(new Error('new Octokit({agent}) is deprecated. Use {request: {agent}} instead. See https://github.com/octokit/rest.js#client-options'))
clientDefaults.request.agent = options.agent
}

if (options.headers) {
console.warn(new Error('new Octokit({headers}) is deprecated. Use {userAgent, previews} instead. See https://github.com/octokit/rest.js#client-options'))
log.warn(new Error('new Octokit({headers}) is deprecated. Use {userAgent, previews} instead. See https://github.com/octokit/rest.js#client-options'))
}

const userAgentOption = clientDefaults.headers['user-agent']
Expand Down
2 changes: 1 addition & 1 deletion plugins/authentication-deprecated/authenticate.js
@@ -1,7 +1,7 @@
module.exports = authenticate

function authenticate (state, options) {
console.warn(new Error('octokit.authenticate() is deprecated. Use "auth" constructor option instead.'))
state.octokit.log.warn(new Error('octokit.authenticate() is deprecated. Use "auth" constructor option instead.'))

if (!options) {
state.auth = false
Expand Down
2 changes: 1 addition & 1 deletion plugins/authentication-deprecated/index.js
Expand Up @@ -7,7 +7,7 @@ const requestError = require('./request-error')
function authenticationPlugin (octokit, options) {
if (options.auth) {
octokit.authenticate = () => {
console.warn(new Error('octokit.authenticate() is deprecated and has no effect when "auth" option is set on Octokit constructor'))
octokit.log.warn(new Error('octokit.authenticate() is deprecated and has no effect when "auth" option is set on Octokit constructor'))
}
return
}
Expand Down
22 changes: 22 additions & 0 deletions plugins/log/index.js
@@ -0,0 +1,22 @@
module.exports = octokitDebug

function octokitDebug (octokit) {
octokit.hook.wrap('request', (request, options) => {
octokit.log.debug(`request`, options)
const start = Date.now()
const requestOptions = octokit.request.endpoint.parse(options)
const path = requestOptions.url.replace(options.baseUrl, '')

return request(options)

.then(response => {
octokit.log.info(`${requestOptions.method} ${path} - ${response.status} in ${Date.now() - start}ms`)
return response
})

.catch(error => {
octokit.log.info(`${requestOptions.method} ${path} - ${error.status} in ${Date.now() - start}ms`)
throw error
})
})
}
2 changes: 1 addition & 1 deletion plugins/register-endpoints/register-endpoints.js
Expand Up @@ -25,7 +25,7 @@ function registerEndpoints (octokit, routes) {

if (apiOptions.deprecated) {
octokit[namespaceName][apiName] = function () {
console.warn(apiOptions.deprecated)
octokit.log.warn(apiOptions.deprecated)
octokit[namespaceName][apiName] = request
return request.apply(null, arguments)
}
Expand Down
15 changes: 15 additions & 0 deletions scripts/templates/index.d.ts.tpl
Expand Up @@ -47,6 +47,12 @@ declare namespace Octokit {
userAgent?: string;
previews?: string[];
baseUrl?: string;
log?: {
debug?: (message: string, info?: object) => void
info?: (message: string, info?: object) => void
warn?: (message: string, info?: object) => void
error?: (message: string, info?: object) => void
};
request?: {
agent?: http.Agent;
timeout?: number
Expand Down Expand Up @@ -83,6 +89,13 @@ declare namespace Octokit {
request?: { [option: string]: any }
}

export interface Log {
debug: (message: string, additionalInfo?: object) => void
info: (message: string, additionalInfo?: object) => void
warn: (message: string, additionalInfo?: object) => void
error: (message: string, additionalInfo?: object) => void
};

export interface Endpoint {
(
Route: string,
Expand Down Expand Up @@ -267,6 +280,8 @@ declare class Octokit {

paginate: Octokit.Paginate;

log: Octokit.Log;

{{#namespaces}}
{{namespace}}: {
{{#methods}}
Expand Down

0 comments on commit cbb5c41

Please sign in to comment.