Completed
Push — master ( f0daaf...26347d )
by Ivan
8s
created

index.js ➔ ???   A

Complexity

Conditions 4
Paths 16

Size

Total Lines 18

Duplication

Lines 0
Ratio 0 %

Importance

Changes 10
Bugs 0 Features 0
Metric Value
cc 4
c 10
b 0
f 0
nc 16
nop 1
dl 0
loc 18
rs 9.2
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
      this._client = errorReporter.start(Object.assign({}, options, {
30
        ignoreEnvironmentCheck: true,
31
      }));
32
    }
33
  }
34
35
  /**
36
   * Send entry to Stackdriver Error Reporting.
37
   *
38
   * @param {String} level - Winston log entry level.
39
   * @param {String} message - Log entry message.
40
   * @param {Object} meta - Winston meta information.
41
   * @param {Function} callback - Callback to let Winston know we are done.
42
   */
43
  log(level, message, meta, callback) {
44
45
    const promise = (this.mode === 'api')
46
      ? this.logApi(level, message, meta)
47
      : this.logConsole(level, message, meta);
48
49
    promise
50
      .then(() => callback(null, true))
51
      .catch((error) => callback(error, false));
52
  }
53
54
  /**
55
   * Send entry to Stackdriver Error Reporting with a help of console.log().
56
   *
57
   * @param {String} level - Winston log entry level.
58
   * @param {String} message - Log entry message.
59
   * @param {Object} meta - Winston meta information.
60
   * @return {Promise}
61
   */
62
  logConsole(level, message, meta) {
63
64
    const eventTime = (new Date()).toISOString();
65
    const errors = this.extractErrorsFromMeta(meta);
66
    errors.forEach((error) => {
67
68
      const stackTrace = Array.isArray(error.stack) ? error.stack.join("\n") : error.stack;
69
      const message = JSON.stringify({
70
        eventTime,
71
        message: stackTrace,
72
        severity: level.toUpperCase(),
73
        serviceContext: this.serviceContext,
74
      });
75
      if (meta.context) {
76
        message.context = meta.context;
77
      }
78
79
      console.log(message);
80
    });
81
82
    return Promise.resolve();
83
  }
84
85
  /**
86
   * Send entry to Stackdriver Error Reporting with a help of API.
87
   *
88
   * @param {String} level - Winston log entry level.
89
   * @param {String} message - Log entry message.
90
   * @param {Object} meta - Winston meta information.
91
   * @return {Promise}
92
   */
93
  logApi(level, message, meta) {
94
95
    const promises = [];
96
97
    const errors = this.extractErrorsFromMeta(meta);
98
    errors.forEach((error) => {
99
100
      const stackTrace = Array.isArray(error.stack) ? error.stack.join("\n") : error.stack;
101
      promises.push(
102
        new Promise((resolve, reject) => {
103
          let request = null;
104
          if (meta.context && meta.context.httpRequest) {
105
            request = meta.context.httpRequest;
106
          }
107
          this._client.report(error, request, stackTrace, (reportError) => {
108
            if (reportError) {
109
              console.log('Failed to send error to Stackdriver Error Reporting.', reportError);
110
              return reject(reportError);
111
            }
112
            return resolve();
113
          })
114
        })
115
      );
116
    });
117
118
    return Promise.all(promises);
119
  }
120
121
  /**
122
   * Extracts all errors from Winston metadata.
123
   *
124
   * @param {Object} data - Winston meta data.
125
   * @return {Array}
126
   */
127
  extractErrorsFromMeta(data) {
128
129
    let errors = [];
130
131
    if (data.stack) {
132
      errors.push(data);
133
    } else if (isPlainObject(data)) {
134
      forOwn(data, (value) => {
135
        errors = errors.concat(this.extractErrorsFromMeta(value));
136
      });
137
    } else if (Array.isArray(data)) {
138
      data.forEach((value) => {
139
        errors = errors.concat(this.extractErrorsFromMeta(value));
140
      });
141
    }
142
143
    return errors;
144
  }
145
}
146
147
export default StackdriverErrorReporting