Sails with AWS XRAY

605 views Asked by At

How in the world is one supposed to install AWS XRAY with Sails?

I'm attempting to translate the installation instructions to Sails' preferred ways of using Express middleware, but I'm falling flat on my face.

Most people will instantly start with "use config/http.js" to configure middleware. Well, that doesn't work in my case, because my API is consumed exclusively with Sails.io (sockets), so the http middleware config is never used.

So now, the logical step is to use policies. Well, if you've read the XRAY instructions, you know that they are trying to capture ALL requests to the app, which requires "start" and "stop" function calls, before and after routes have been configured. So, policies don't work.

So, my next step was to attempt it in the app.js, and the config/bootstrap.js files, to no avail, probably because I can't easily get the Express instance Sails is using. So, is it even possible with Sails' current config options? Anyone have any clue how to accomplish this?

1

There are 1 answers

0
NeoNexus DeMortis On BEST ANSWER

To anyone that should stumble upon this, attempting to integrate AWS X-Ray into Sails.js:

I finally got it working, by building a project hook for it. If someone is ambitious enough, they are more then welcome to make it an installable hook.

IMPORTANT NOTES

  • The hook is designed to only run when the environment variable AWS_XRAY === 'yes'. This is a safety trap, to prevent local and CI machines from running XRAY.

  • The hook taps into the "before" part of the route setup. What this means is: "before routes are instantiated, use this middleware".

  • This code is setup to ignore the route "/_ping" (for X-Ray, it'll let the request complete as normal), which is used for ELB health checks. These do not need to be logged on X-Ray, they are just a waste of money. I HIGHLY recommend you read through this code, and adjust as needed. Especially the req.headers.host and req.connection "fixes". This was the only way I could get X-Ray to work, without changing the repo's code (still can't find the Github repo for it).

  • The req.connection.encrypted injection is just to have X-Ray report the URL as https. It's not important, unless you want your traces to reflect the correct URL.

  • Because we use CloudFlare, there are additional catches to collect the end-user's IP address for requests. This should have no affect if you don't use CF, and should not require any modification. But, I have to ask, WHY aren't use using CF?

  • This has only gotten me so far, and I can only see basic data about requests in the X-Ray console. I can't yet see database queries, or other services that are in use.

RESULTS MAY VARY

Don't forget!

  • npm i aws-xray-sdk --save.
  • To install and run the X-Ray Daemon

This is the code I put together api/hooks/setup-aws-xray.js:

var AWSXRay = require('aws-xray-sdk');

module.exports = function setupAwsXray(sails){
    var setupXray = false;

    function injectXrayIfRequested(req, res, next){
        if (
            setupXray
                && !req.segment
                && req.path !== '/_ping'
        ) {
            req.headers.host = (sails.config.environment === 'production')
                ? 'myapp.com'
                : 'dev.myapp.com';
            req.connection = {
                remoteAddress: req.headers['http_cf_connecting_ip']
                    || req.headers['HTTP_CF_CONNECTING_IP']
                    || req.headers['X-Real-IP']
                    || req.ip,
                encrypted: true
            };

            AWSXRay.express.openSegment()(req, res, next); // not a mistake
        } else {
            next();
        }
    }

    // This just allows us to get a handle on req.segment.
    // This is important if you want to add annotations / metadata.

    // Despite AWS's documentation, you DO NOT need to close segments
    // when using manual mode and express.openSegment, it will
    // do this for you automatically.
    AWSXRay.enableManualMode(); 

    return {
        configure: function(){
            if (process.env.AWS_XRAY && process.env.AWS_XRAY === 'yes') {
                setupXray = true;

                AWSXRay.setDefaultName('myapp_' + sails.config.environment);
            }
        },
        routes: {
            before: {
                '/*': injectXrayIfRequested
            }
        }
    };
};