Search code examples
node.jsangularwebpacknewrelicangular-universal

Integrating newrelic on angular universal application (nodejs)


I'm integrating newrelic on nodejs app, made with angular universal. I am using webpack for bundling

first line in main.server.aot.ts

const newrelic = require('newrelic');

and have added newrelic.js in root directory as well.

running build ejects following error :-

ERROR in ./node_modules/@newrelic/native-metrics/lib/pre-build.js
Module not found: Error: Can't resolve '../package' in 'D:\repos\ib-mobile\node_modules\@newrelic\native-metrics\lib'
 @ ./node_modules/@newrelic/native-metrics/lib/pre-build.js 40:12-33
 @ ./node_modules/@newrelic/native-metrics/index.js
 @ ./node_modules/newrelic/lib/sampler.js
 @ ./node_modules/newrelic/lib/agent.js
 @ ./node_modules/newrelic/index.js
 @ ./src/main.server.aot.ts

ERROR in ./node_modules/node-gyp/lib/node-gyp.js
Module not found: Error: Can't resolve '../package' in 'D:\repos\ib-mobile\node_modules\node-gyp\lib'
 @ ./node_modules/node-gyp/lib/node-gyp.js 67:16-37
 @ ./node_modules/@newrelic/native-metrics/lib/pre-build.js
 @ ./node_modules/@newrelic/native-metrics/index.js
 @ ./node_modules/newrelic/lib/sampler.js
 @ ./node_modules/newrelic/lib/agent.js
 @ ./node_modules/newrelic/index.js
 @ ./src/main.server.aot.ts

ERROR in ./node_modules/newrelic/index.js
Module not found: Error: Can't resolve './package' in 'D:\repos\ib-mobile\node_modules\newrelic'
 @ ./node_modules/newrelic/index.js 13:19-39
 @ ./src/main.server.aot.ts

and if I add newrelic as externals in webpack config

module.exports = {
  entry: root('./src/main.server.aot.ts'),
  output: {
    path: root('dist_server'),
    filename: 'server.js'
  },
  target: 'node',
  externals: {
    newrelic: true
  }
};

then I get another error as

/home/ubuntu/ib-mobile/dist_server/server.js:79752

module.exports = newrelic;
                 ^

ReferenceError: newrelic is not defined
    at Object.<anonymous> (/home/ubuntu/ib-mobile/dist_server/server.js:79752:18)
    at __webpack_require__ (/home/ubuntu/ib-mobile/dist_server/server.js:26:30)
    at Object.module.exports (/home/ubuntu/ib-mobile/dist_server/server.js:79667:16)
    at __webpack_require__ (/home/ubuntu/ib-mobile/dist_server/server.js:26:30)
    at /home/ubuntu/ib-mobile/dist_server/server.js:91:18
    at Object.<anonymous> (/home/ubuntu/ib-mobile/dist_server/server.js:94:10)
    at Module._compile (module.js:570:32)
    at Object.Module._extensions..js (module.js:579:10)
    at Module.load (module.js:487:32)
    at tryModuleLoad (module.js:446:12)

Angular Version (ng -v)

Angular CLI: 1.6.3
Node: 6.11.0
OS: win32 x64
Angular: 4.4.6
... animations, common, compiler, compiler-cli, core, forms
... http, language-service, platform-browser
... platform-browser-dynamic, platform-server, router
... tsc-wrapped

@angular/cli: 1.6.3
@angular-devkit/build-optimizer: 0.0.36
@angular-devkit/core: 0.0.22
@angular-devkit/schematics: 0.0.42
@ngtools/json-schema: 1.1.0
@ngtools/webpack: 1.9.3
@schematics/angular: 0.1.11
@schematics/schematics: 0.0.11
typescript: 2.3.4
webpack: 3.10.0

Solution

  • Found a hacky way to get around this issuse!

    TLDR

    Essentially all this does is configure webpack to add the import at the top of the server.js file, and BAM it works.

    Problem

    So everything that you have done is correct, however by telling webpack that you want newrelic module to be external, you are telling it that the newrelic instance will be available in memory when running the application. We obviously have not done this and that is why we are getting the error you mentioned above. I think the use case for this functionality is for the browser, where you are including scripts with the <script src="{http://example.com/script.js}" /> and so you dont want webpack to concern itself with trying to resolve the dependency from node_modules.

    Solution

    Instead of using the "externals" property, we are going to simply add our standard nodejs import var newrelic = require('newrelic'); into the file that runs the expressjs node application (exactly like the new relic people are expecting).

    In order to do this, we need the following:

    • BannerWebpackPlugin
    • Knowledge of which output chunk/file our nodejs express application is running.

    In order to install BannerWebpackPlugin simply do the following: npm install --save-dev banner-webpack-plugin

    As for the chunk/file you can either have a look at the error log (you can see from this error snippet that its the server.js chunk/file):

    ReferenceError: newrelic is not defined at Object.<anonymous> (/home/ubuntu/ib-mobile/dist_server/server.js:79752:18)

    Or you could look the webpack files "entry" property and find the chunk that is being generated by webpack (In my case this is also a "sever" which outputs to server.js.)

    Now that we have this information we simply need to add a banner configuration to our chunk through the webpack config plugins section.

    This is what my one looked like:

    plugins: [
    ...,
        new BannerPlugin({
            chunks: {
                server:{
                    beforeContent: 'var newrelic = require("newrelic");'
                }
           }
        }),
    ...]
    

    At this point you should be able to run your webpack build and have your new relic stuff working!

    If you had previously been including a require in your application code you can now remove it and the webpack import should be enough to get the newrelic agent running.