text: /**
   * Copyright 2015 Google Inc. All Rights Reserved.
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *      http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  'use strict';
  
  const shimmer = require('shimmer');
  var urlParse = require('url').parse;
  
  function startSpanForRequest(api, req, res, next) {
    var originalEnd = res.end;
    var options = {
      name: urlParse(req.url).pathname,
      url: req.url,
      traceContext: req.headers[api.constants.TRACE_CONTEXT_HEADER_NAME],
      ip: req.connection.remoteAddress,
      method: req.method,
      skipFrames: 4
    };
    api.runInRootSpan(options, function(root) {
      // Set response trace context.
      var responseTraceContext =
        api.getResponseTraceContext(options.traceContext, !!root);
      if (responseTraceContext) {
        res.setHeader(api.constants.TRACE_CONTEXT_HEADER_NAME, responseTraceContext);
      }
      
      if (!root) {
        return;
      }
  
      api.wrapEmitter(req);
      api.wrapEmitter(res);
  
      const url = (req.headers['X-Forwarded-Proto'] || 'http') +
        '://' + req.headers.host + req.url;
  
      // we use the path part of the url as the span name and add the full
      // url as a label
      // req.path would be more desirable but is not set at the time our middlewear runs.
      root.addLabel(api.labels.HTTP_METHOD_LABEL_KEY, req.method);
      root.addLabel(api.labels.HTTP_URL_LABEL_KEY, url);
      root.addLabel(api.labels.HTTP_SOURCE_IP, req.connection.remoteAddress);
  
      // wrap end
      res.end = function(chunk, encoding) {
        res.end = originalEnd;
        const returned = res.end(chunk, encoding);
  
        if (req.route && req.route.path) {
          root.addLabel(
            'koa/request.route.path', req.route.path);
        }
        root.addLabel(
            api.labels.HTTP_RESPONSE_CODE_LABEL_KEY, res.statusCode);
        root.endSpan();
  
        return returned;
      };
      api.wrap(next);
    });
  }
  
  function createMiddleware(api) {
    return function* middleware(next) {
      /* jshint validthis:true */
      const req = this.req;
      const res = this.res;
  
      startSpanForRequest(api, req, res, next);
  
      yield next;
    };
  }
  
  function createMiddleware2x(api) {
    return function middleware(ctx, next) {
      const req = ctx.req;
      const res = ctx.res;
  
      startSpanForRequest(api, req, res, next);
  
      return next();
    };
  }
  
  function patchUse(koa, api, createMiddlewareFunction) {
    shimmer.wrap(koa.prototype, 'use', function useWrap(use) {
      return function useTrace() {
        if (!this._google_trace_patched) {
          this._google_trace_patched = true;
          this.use(createMiddlewareFunction(api));
        }
        return use.apply(this, arguments);
      };
    });
  }
  
  module.exports = [
    {
      file: '',
      versions: '1.x',
      patch: function(koa, api) {
        patchUse(koa, api, createMiddleware);
      },
      unpatch: function(koa) {
        shimmer.unwrap(koa.prototype, 'use');
      }
    },
    {
      file: '',
      versions: '2.x',
      patch: function(koa, api) {
        patchUse(koa, api, createMiddleware2x);
      },
      unpatch: function(koa) {
        shimmer.unwrap(koa.prototype, 'use');
      }
    }
  ];
  