node-analytics.js
function sessionData(req, res, session, cb){

    const now = new Date();

    if(session.continued)
        return cb(null, req, res, session);

    async.parallelLimit([
            getIp,
            getLocation,
            getSystem
        ], 2,
        function(err){
            cb(err, this.req, this.res, this.session);

            if(opts.log_all)
                log.timer('sessionData', now);

        }.bind({
            req: req,
            res: res,
            session: session
        })
    );

    // ======================

    // .ip
    function getIp(cb){
        session.ip = get_ip(req).clientIp;
        cb(null)
    }

    // .geo :: .city, .state, .country
    function getLocation(cb){
        if(!geo_lookup)
            return cb(null);

        const loc = geo_lookup.get(session.ip);

        if(!session.geo)
            session.geo = {};


        if(loc){
            try {
                if(loc.city)
                    session.geo.city = loc.city.names.en;
                if(loc.subdivisions)
                    session.geo.state = loc.subdivisions[0].iso_code;
                if(loc.country)
                    session.geo.country = loc.country.iso_code;
                if(loc.continent)
                    session.geo.continent = loc.continent.code;
                if(loc.location)
                    session.geo.time_zone = loc.location.time_zone;
            }
            catch(e){
                log.error('geoIP error:', e);
            }
        }

        cb(null)
    }

    // .system :: .os{, .broswer{ .name, .version
    function getSystem(cb){
        var agent = useragent.parse(req.headers['user-agent']);
        var os = agent.os;

        if(!session.system)
            session.system = {};

        if(!session.system.browser)
            session.system.browser = {};
        if(!session.system.os)
            session.system.os = {};

        session.system.browser.name = agent.family;
        session.system.browser.version = agent.major + '.' + agent.minor + '.' + agent.patch;

        session.system.os.name = os.family;
        session.system.os.version = os.major + '.' + os.minor + '.' + os.patch;

        cb(null)
    }
}
function sessionData(req, res, session, cb){

    const now = new Date();

    if(session.continued)
        return cb(null, req, res, session);

    async.parallelLimit([
            getIp,
            getLocation,
            getSystem
        ], 2,
        function(err){
            cb(err, this.req, this.res, this.session);

            if(opts.log_all)
                log.timer('sessionData', now);

        }.bind({
            req: req,
            res: res,
            session: session
        })
    );

    // ======================

    // .ip
    function getIp(cb){
        session.ip = get_ip(req).clientIp;
        cb(null)
    }

    // .geo :: .city, .state, .country
    function getLocation(cb){
        if(!geo_lookup)
            return cb(null);

        const loc = geo_lookup.get(session.ip);

        if(!session.geo)
            session.geo = {};


        if(loc){
            try {
                if(loc.city)
                    session.geo.city = loc.city.names.en;
                if(loc.subdivisions)
                    session.geo.state = loc.subdivisions[0].iso_code;
                if(loc.country)
                    session.geo.country = loc.country.iso_code;
                if(loc.continent)
                    session.geo.continent = loc.continent.code;
                if(loc.location)
                    session.geo.time_zone = loc.location.time_zone;
            }
            catch(e){
                log.error('geoIP error:', e);
            }
        }

        cb(null)
    }

    // .system :: .os{, .broswer{ .name, .version
    function getSystem(cb){
        var agent = useragent.parse(req.headers['user-agent']);
        var os = agent.os;

        if(!session.system)
            session.system = {};

        if(!session.system.browser)
            session.system.browser = {};
        if(!session.system.os)
            session.system.os = {};

        session.system.browser.name = agent.family;
        session.system.browser.version = agent.major + '.' + agent.minor + '.' + agent.patch;

        session.system.os.name = os.family;
        session.system.os.version = os.major + '.' + os.minor + '.' + os.patch;

        cb(null)
    }
}
append session data