Completed
Push — master ( 8f831f...8d3a90 )
by Ivan
01:09
created

index.js ➔ ???   B

Complexity

Conditions 4
Paths 16

Size

Total Lines 22

Duplication

Lines 0
Ratio 0 %

Importance

Changes 8
Bugs 0 Features 0
Metric Value
c 8
b 0
f 0
nc 16
nop 1
dl 0
loc 22
cc 4
rs 8.9197
1
import forOwn from 'for-own'
2
import isPlainObject from 'is-plain-object'
3
import errorReporter from '@google/cloud-errors'
4
import {Transport} from 'winston/lib/winston/transports/transport'
5
6
/**
7
 * Winston transport for Stackdriver Error Reporting service.
8
 */
9
class StackdriverErrorReporting extends Transport {
10
11
  /**
12
   * Class constructor.
13
   *
14
   * @param {Object} options Configuration options.
15
   */
16
  constructor(options = {}) {
17
18
    super(options);
19
20
    this.name = 'StackdriverErrorReporting';
21
    this.level = options.level || 'error';
22
    this.mode = options.mode.toLowerCase() || 'api';
23
    this.serviceContext = options.serviceContext || {service: 'unknown', version: 'unknown'};
24
    if (this.mode !== 'console' && this.mode !== 'api') {
25
      throw new Error('StackdriverErrorReporting: parameter "mode" is expected to be "console" or "api".');
26
    }
27
28
    if (this.mode === 'api') {
29
      // NODE_ENV hack is required until
30
      // https://github.com/GoogleCloudPlatform/cloud-errors-nodejs/issues/79
31
      // will be resolved
32
      const previousNodeEnv = process.env.NODE_ENV;
33
      process.env.NODE_ENV = 'production';
34
      this._client = errorReporter.start(options);
35
      process.env.NODE_ENV = previousNodeEnv;
36
    }
37
  }
38
39
  /**
40
   * Send entry to Stackdriver Error Reporting.
41
   *
42
   * @param {String} level - Winston log entry level.
43
   * @param {String} message - Log entry message.
44
   * @param {Object} meta - Winston meta information.
45
   * @param {Function} callback - Callback to let Winston know we are done.
46
   */
47
  log(level, message, meta, callback) {
48
49
    const promise = (this.mode === 'api')
50
      ? this.logApi(level, message, meta)
51
      : this.logConsole(level, message, meta);
52
53
    promise
54
      .then(() => callback(null, true))
55
      .catch((error) => callback(error, false));
56
  }
57
58
  /**
59
   * Send entry to Stackdriver Error Reporting with a help of console.log().
60
   *
61
   * @param {String} level - Winston log entry level.
62
   * @param {String} message - Log entry message.
63
   * @param {Object} meta - Winston meta information.
64
   * @return {Promise}
65
   */
66
  logConsole(level, message, meta) {
67
68
    const eventTime = (new Date()).toISOString();
69
    const errors = this.extractErrorsFromMeta(meta);
70
    errors.forEach((error) => {
71
72
      const stackTrace = Array.isArray(error.stack) ? error.stack.join("\n") : error.stack;
73
      const message = JSON.stringify({
74
        eventTime,
75
        message: stackTrace,
76
        severity: level.toUpperCase(),
77
        serviceContext: this.serviceContext,
78
      });
79
      if (meta.context) {
80
        message.context = meta.context;
81
      }
82
83
      console.log(message);
1 ignored issue
show
Debugging Code introduced by
console.log looks like debug code. Are you sure you do not want to remove it?
Loading history...
84
    });
85
86
    return Promise.resolve();
87
  }
88
89
  /**
90
   * Send entry to Stackdriver Error Reporting with a help of API.
91
   *
92
   * @param {String} level - Winston log entry level.
93
   * @param {String} message - Log entry message.
94
   * @param {Object} meta - Winston meta information.
95
   * @return {Promise}
96
   */
97
  logApi(level, message, meta) {
98
99
    const promises = [];
100
101
    const errors = this.extractErrorsFromMeta(meta);
102
    errors.forEach((error) => {
103
104
      const stackTrace = Array.isArray(error.stack) ? error.stack.join("\n") : error.stack;
105
      promises.push(
106
        new Promise((resolve, reject) => {
107
          let request = null;
108
          if (meta.context && meta.context.httpRequest) {
109
            request = meta.context.httpRequest;
110
          }
111
          this._client.report(error, request, stackTrace, (reportError) => {
112
            if (reportError) {
113
              console.log('Failed to send error to Stackdriver Error Reporting.', reportError);
114
              return reject(reportError);
115
            }
116
            return resolve();
117
          })
118
        })
119
      );
120
    });
121
122
    return Promise.all(promises);
123
  }
124
125
  /**
126
   * Extracts all errors from Winston metadata.
127
   *
128
   * @param {Object} data - Winston meta data.
129
   * @return {Array}
130
   */
131
  extractErrorsFromMeta(data) {
132
133
    const errors = [];
134
135
    if (isPlainObject(data)) {
136
      if (data.stack) {
137
        errors.push(data);
138
      } else {
139
        forOwn(data, (value) => {
140
          errors.concat(this.extractErrorsFromMeta(value));
141
        });
142
      }
143
    } else if (Array.isArray(data)) {
144
      data.forEach((value) => {
145
        errors.concat(this.extractErrorsFromMeta(value));
146
      });
147
    }
148
149
    return errors;
150
  }
151
}
152
153
export default StackdriverErrorReporting