# all files / raven-node/lib/ breadcrumbs.js

93.85% Statements 61/65
78.26% Branches 18/23
94.44% Functions 17/18
93.85% Lines 61/65

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161

    1×
    1×

    1×
    19×
    19×
    19×
    19×

    1×

    1×

    2×
    10×
    2×

    8×
    8×

    8×
    2×

    2×

    2×

    2×

    3×
    3×
    3×

    3×
    3×

    3×
    3×

    3×

    3×

    3×
    2×
    2×
    8×
    2×

    8×

    3×
    3×
    3×

    3×
    3×
    1×

    3×
    3×
    1×
    1×
    1×

    1×
    1×
    1×

    1×

    1×
    6×
    6×
    6×

    1×
    8×

    8×
    20×
    20×
    20×
    20×

    1×

     'use strict';

    var util = require('util');
    var utils = require('./utils.js');

    /**
     * Polyfill a method
     * @param obj object e.g. `document`
     * @param name method name present on object e.g. `addEventListener`
     * @param replacement replacement function
     * @param track {optional} record instrumentation to an array
     */
    function fill(obj, name, replacement, track) {
      var orig = obj[name];
      obj[name] = replacement(orig);
      Eif (track) {
        track.push([obj, name, orig]);
      }
    }

    var originals = [];

    var wrappers = {
      console: function (Raven) {
        var wrapConsoleMethod = function (level) {
          if (!(level in console)) {
            return;
          }

          fill(console, level, function (originalConsoleLevel) {
            var sentryLevel = level === 'warn'
                ? 'warning'
                : level;

            return function () {
              var args = [].slice.call(arguments);

              Raven.captureBreadcrumb({
                message: util.format.apply(null, args),
                level: sentryLevel,
                category: 'console'
              });

              originalConsoleLevel.apply(console, args);
            };
          }, originals);
        };

        ['debug', 'info', 'warn', 'error', 'log'].forEach(wrapConsoleMethod);
      },

      http: function (Raven) {
        var http = require('http');
        var OrigClientRequest = http.ClientRequest;
        var ClientRequest = function (options, cb) {
          // Note: this won't capture a breadcrumb if a response never comes
          // It would be useful to know if that was the case, though, so
          // todo: revisit to see if we can capture sth indicating response never came
          // possibility: capture one breadcrumb for "req sent" and one for "res recvd"
          // seems excessive but solves the problem and *is* strictly more information
          // could be useful for weird response sequencing bug scenarios
          var self = this;
          OrigClientRequest.call(self, options, cb);

          // We could just always grab these from self.agent, self.method, self._headers, self.path, etc
          // but certain other http-instrumenting libraries (like nock, which we use for tests) fail to
          // maintain the guarantee that after calling OrigClientRequest, those fields will be populated
          var url, method;
          Iif (typeof options === 'string') {
            url = options;
            method = 'GET';
          } else {
            url = (options.protocol || '') + '//' +
                  (options.hostname || options.host || '') +
                  (options.path || '/');
            method = options.method || 'GET';
          }

          // Don't capture breadcrumb for our own requests
          if (!Raven.dsn || url.indexOf(Raven.dsn.host) === -1) {
            fill(self, 'emit', function (origEmit) {
              return function (evt, maybeResp) {
                if (evt === 'response') {
                  Raven.captureBreadcrumb({
                    type: 'http',
                    category: 'http',
                    data: {
                      method: method,
                      url: url,
                      status_code: maybeResp.statusCode
                    }
                  });
                }
                return origEmit.apply(this, arguments);
              };
            }, originals);
          }
        };
        util.inherits(ClientRequest, OrigClientRequest);
        fill(http, 'ClientRequest', function () {
          return ClientRequest;
        }, originals);

        // http.request orig refs module-internal ClientRequest, not exported one, so
        // it still points at orig ClientRequest after our monkeypatch; these reimpls
        // just get that reference updated to use our new ClientRequest
        fill(http, 'request', function () {
          return function (options, cb) {
            return new http.ClientRequest(options, cb);
          };
        }, originals);

        fill(http, 'get', function () {
          return function (options, cb) {
            var req = http.request(options, cb);
            req.end();
            return req;
          };
        }, originals);
      },

      pg: function (Raven) {
        // Using fill helper here is hard because of `this` binding
        var pg = require('pg');
        var origQuery = pg.Connection.prototype.query;
        pg.Connection.prototype.query = function (text) {
          Raven.captureBreadcrumb({
            category: 'postgres',
            message: text
          });
          origQuery.call(this, text);
        };
        originals.push([pg.Connection.prototype, 'query', origQuery]);
      }
    };

    function instrument(key, Raven) {
      try {
        wrappers[key](Raven);
        utils.consoleAlert('Enabled automatic breadcrumbs for ' + key);
      } catch (e) {
        // associated module not available
      }
    }

    function restoreOriginals() {
      var original;
      // eslint-disable-next-line no-cond-assign
      while (original = originals.shift()) {
        var obj = original[0];
        var name = original[1];
        var orig = original[2];
        obj[name] = orig;
      }
    }

    module.exports = {
      instrument: instrument,
      restoreOriginals: restoreOriginals
    };