1
0
mirror of https://github.com/bpatrik/pigallery2.git synced 2025-12-01 22:52:06 +02:00

improving logging and configs

This commit is contained in:
Braun Patrik
2017-06-04 15:25:08 +02:00
parent 98681322f5
commit 7e56ef1111
36 changed files with 279 additions and 326 deletions

View File

@@ -7,8 +7,7 @@ declare module 'winston' {
}
}
export const Logger = new winston.Logger({
export const winstonSettings = {
transports: [
new winston.transports.Console({
level: 'silly',
@@ -29,4 +28,6 @@ export const Logger = new winston.Logger({
})
],
exitOnError: false
});
};
export const Logger = new winston.Logger(winstonSettings);

View File

@@ -1,5 +1,5 @@
import * as path from "path";
import {Config} from "./config/Config";
import {Config} from "../common/config/private/Config";
class ProjectPathClass {
public Root: string;

View File

@@ -1,28 +0,0 @@
import {ConfigLoader} from "./ConfigLoader";
import * as path from "path";
import {ConfigClass, DatabaseType} from "../../common/config/Config";
export let Config = new ConfigClass();
Config.Server = {
port: 80,
imagesFolder: "demo/images",
thumbnail: {
folder: "demo/TEMP",
hardwareAcceleration: true
},
database: {
type: DatabaseType.mysql,
mysql: {
host: "localhost",
username: "root",
password: "root",
database: "pigallery2"
}
}
};
ConfigLoader.init(Config, path.join(__dirname, './../../config.json'), [["PORT", "Server-port"]]);

View File

@@ -1,116 +0,0 @@
import * as fs from "fs";
import * as optimist from "optimist";
export class ConfigLoader {
static init(configObject: any, configFilePath?: string, envAlias: Array<Array<string>> = []) {
this.processConfigFile(configFilePath, configObject);
this.processArguments(configObject);
this.processEnvVariables(configObject, envAlias);
}
private static processEnvVariables(configObject: any, envAlias: Array<Array<string>>) {
let varAliases = {};
envAlias.forEach((alias) => {
if (process.env[alias[0]]) {
varAliases[alias[1]] = process.env[alias[0]];
}
});
this.processHierarchyVar(configObject, varAliases);
this.processHierarchyVar(configObject, process.env);
};
private static processArguments(configObject: any) {
let argv = optimist.argv;
delete(argv._);
delete(argv.$0);
this.processHierarchyVar(configObject, argv);
};
private static processHierarchyVar(configObject: any, vars: any) {
let config = {};
Object.keys(vars).forEach((key) => {
let keyArray = key.split("-");
let value = vars[key];
//recursive settings
let setObject = (object: any, keyArray: Array<string>, value: any): void => {
let key = keyArray.shift();
object[key] = object[key] || {};
if (keyArray.length == 0) {
//convert to boolean
if (value.toLowerCase && value.toLowerCase() === "false") {
value = false;
}
if (value.toLowerCase && value.toLowerCase() === "true") {
value = true;
}
object[key] = value;
return;
}
return setObject(object[key], keyArray, value);
};
setObject(config, keyArray, value);
});
this.loadObject(configObject, config);
}
private static processConfigFile(configFilePath: string, configObject: any) {
if (typeof configFilePath !== 'undefined') {
if (ConfigLoader.loadConfigFile(configFilePath, configObject) === false) {
ConfigLoader.saveConfigFile(configFilePath, configObject);
}
}
};
private static loadConfigFile(configFilePath: string, configObject: any): boolean {
if (fs.existsSync(configFilePath) === false) {
return false;
}
try {
let config = JSON.parse(fs.readFileSync(configFilePath, 'utf8'));
this.loadObject(configObject, config);
return true;
} catch (err) {
}
return false;
}
private static saveConfigFile(configFilePath: string, configObject: any) {
try {
fs.writeFileSync(configFilePath, JSON.stringify(configObject, null, 4));
} catch (err) {
}
}
private static loadObject(targetObject, sourceObject) {
Object.keys(sourceObject).forEach((key) => {
if (typeof targetObject[key] === "undefined") {
return;
}
if (Array.isArray(targetObject[key])) {
return targetObject[key] = sourceObject[key];
}
if (typeof targetObject[key] === "object") {
return this.loadObject(targetObject[key], sourceObject[key]);
}
targetObject[key] = sourceObject[key];
});
}
}

View File

@@ -8,9 +8,9 @@ import {AutoCompleteItem, SearchTypes} from "../../common/entities/AutoCompleteI
import {ContentWrapper} from "../../common/entities/ConentWrapper";
import {SearchResultDTO} from "../../common/entities/SearchResult";
import {PhotoDTO} from "../../common/entities/PhotoDTO";
import {Config} from "../config/Config";
import {ProjectPath} from "../ProjectPath";
import {Logger} from "../Logger";
import {Config} from "../../common/config/private/Config";
const LOG_TAG = "[GalleryMWs]";

View File

@@ -5,12 +5,12 @@ import * as fs from "fs";
import * as os from "os";
import {NextFunction, Request, Response} from "express";
import {Error, ErrorCodes} from "../../../common/entities/Error";
import {Config} from "../../config/Config";
import {ContentWrapper} from "../../../common/entities/ConentWrapper";
import {DirectoryDTO} from "../../../common/entities/DirectoryDTO";
import {ProjectPath} from "../../ProjectPath";
import {PhotoDTO} from "../../../common/entities/PhotoDTO";
import {hardwareRenderer, softwareRenderer} from "./THRenderers";
import {Config} from "../../../common/config/private/Config";
Config.Client.concurrentThumbnailGenerations = Math.max(1, os.cpus().length - 1);
@@ -18,14 +18,21 @@ Config.Client.concurrentThumbnailGenerations = Math.max(1, os.cpus().length - 1)
const Pool = require('threads').Pool;
const pool = new Pool(Config.Client.concurrentThumbnailGenerations);
if (Config.Server.thumbnail.hardwareAcceleration == true) {
pool.run(hardwareRenderer);
} else {
pool.run(softwareRenderer);
}
export class ThumbnailGeneratorMWs {
private static poolsInited = false;
private static initPools() {
if (this.poolsInited == true) {
return
}
if (Config.Server.thumbnail.hardwareAcceleration == true) {
pool.run(hardwareRenderer);
} else {
pool.run(softwareRenderer);
}
this.poolsInited = true;
}
private static addThInfoTODir(directory: DirectoryDTO) {
if (typeof directory.photos == "undefined") {
@@ -111,6 +118,7 @@ export class ThumbnailGeneratorMWs {
}
private static generateImage(imagePath: string, size: number, makeSquare: boolean, req: Request, res: Response, next: NextFunction) {
//generate thumbnail path
@@ -129,6 +137,7 @@ export class ThumbnailGeneratorMWs {
fs.mkdirSync(ProjectPath.ThumbnailFolder);
}
this.initPools();
//run on other thread
pool.send({imagePath: imagePath, size: size, thPath: thPath, makeSquare: makeSquare, __dirname: __dirname})
.on('done', (out) => {

View File

@@ -1,9 +1,9 @@
///<reference path="../customtypings/ExtendedRequest.d.ts"/>
import {NextFunction, Request, Response} from "express";
import {Error, ErrorCodes} from "../../../common/entities/Error";
import {UserRoles, UserDTO} from "../../../common/entities/UserDTO";
import {UserDTO, UserRoles} from "../../../common/entities/UserDTO";
import {ObjectManagerRepository} from "../../model/ObjectManagerRepository";
import {Config} from "../../config/Config";
import {Config} from "../../../common/config/private/Config";
export class AuthenticationMWs {

View File

@@ -2,8 +2,8 @@ import {NextFunction, Request, Response} from "express";
import {Error, ErrorCodes} from "../../../common/entities/Error";
import {ObjectManagerRepository} from "../../model/ObjectManagerRepository";
import {UserDTO} from "../../../common/entities/UserDTO";
import {Config} from "../../config/Config";
import {Utils} from "../../../common/Utils";
import {Config} from "../../../common/config/private/Config";
export class UserMWs {

View File

@@ -138,7 +138,6 @@ pool.run(
try {
for (let i = 0; i < list.length; i++) {
let file = list[i];
console.log(list[i]);
let fullFilePath = path.normalize(path.resolve(directoryInfo.absoluteDirectoryName, file));
if (photosOnly == false && fs.statSync(fullFilePath).isDirectory()) {
let promise = parseDir({
@@ -195,7 +194,7 @@ pool.run(
export class DiskManager {
public static scanDirectory(relativeDirectoryName: string, cb: (error: any, result: DirectoryDTO) => void) {
Logger.silly(LOG_TAG, "scanDirectory");
Logger.silly(LOG_TAG, "scanning directory:", relativeDirectoryName);
let directoryName = path.basename(relativeDirectoryName);
let directoryParent = path.join(path.dirname(relativeDirectoryName), path.sep);
let absoluteDirectoryName = path.join(ProjectPath.ImageFolder, relativeDirectoryName);

View File

@@ -1,10 +1,10 @@
import "reflect-metadata";
import {createConnection, Connection} from "typeorm";
import {Config} from "../../config/Config";
import {Connection, createConnection} from "typeorm";
import {UserEntity} from "./enitites/UserEntity";
import {UserRoles} from "../../../common/entities/UserDTO";
import {PhotoEntity, PhotoMetadataEntity} from "./enitites/PhotoEntity";
import {DirectoryEntity} from "./enitites/DirectoryEntity";
import {Config} from "../../../common/config/private/Config";
export class MySQLConnection {

View File

@@ -2,22 +2,22 @@ import {AuthenticationMWs} from "../middlewares/user/AuthenticationMWs";
import {UserRoles} from "../../common/entities/UserDTO";
export class AdminRouter {
constructor(private app: any) {
public static route(app: any) {
this.addResetDB();
this.addIndexGallery();
this.addResetDB(app);
this.addIndexGallery(app);
}
private addResetDB() {
this.app.post("/api/admin/db/reset",
private static addResetDB(app) {
app.post("/api/admin/db/reset",
AuthenticationMWs.authenticate,
AuthenticationMWs.authorise(UserRoles.Admin)
//TODO: implement
);
};
private addIndexGallery() {
this.app.post("/api/admin/gallery/index",
private static addIndexGallery(app) {
app.post("/api/admin/gallery/index",
AuthenticationMWs.authenticate,
AuthenticationMWs.authorise(UserRoles.Admin)
//TODO: implement

View File

@@ -1,26 +1,27 @@
import {RenderingMWs} from "../middlewares/RenderingMWs";
import {Error, ErrorCodes} from "../../common/entities/Error";
import {Logger} from "../Logger";
import Request = Express.Request;
import Response = Express.Response;
export class ErrorRouter {
constructor(private app: any) {
public static route(app: any) {
this.addApiErrorHandler();
this.addGenericHandler();
this.addApiErrorHandler(app);
this.addGenericHandler(app);
}
private addApiErrorHandler() {
this.app.use("/api/*",
private static addApiErrorHandler(app) {
app.use("/api/*",
RenderingMWs.renderError
);
};
private addGenericHandler() {
this.app.use((err: any, req: Request, res: Response, next: Function) => {
private static addGenericHandler(app) {
app.use((err: any, req: Request, res: Response, next: Function) => {
//Flush out the stack to the console
console.error(err.stack);
Logger.error(err);
next(new Error(ErrorCodes.SERVER_ERROR, "Unknown server side error"));
},
RenderingMWs.renderError

View File

@@ -4,20 +4,20 @@ import {RenderingMWs} from "../middlewares/RenderingMWs";
import {ThumbnailGeneratorMWs} from "../middlewares/thumbnail/ThumbnailGeneratorMWs";
export class GalleryRouter {
constructor(private app: any) {
public static route(app: any) {
this.addGetImageIcon();
this.addGetImageThumbnail();
this.addGetImage();
this.addDirectoryList();
this.addGetImageIcon(app);
this.addGetImageThumbnail(app);
this.addGetImage(app);
this.addDirectoryList(app);
this.addSearch();
this.addInstantSearch();
this.addAutoComplete();
this.addSearch(app);
this.addInstantSearch(app);
this.addAutoComplete(app);
}
private addDirectoryList() {
this.app.get(["/api/gallery/content/:directory(*)", "/api/gallery/", "/api/gallery//"],
private static addDirectoryList(app) {
app.get(["/api/gallery/content/:directory(*)", "/api/gallery/", "/api/gallery//"],
AuthenticationMWs.authenticate,
GalleryMWs.listDirectory,
ThumbnailGeneratorMWs.addThumbnailInformation,
@@ -27,16 +27,16 @@ export class GalleryRouter {
};
private addGetImage() {
this.app.get(["/api/gallery/content/:imagePath(*\.(jpg|bmp|png|gif|jpeg))"],
private static addGetImage(app) {
app.get(["/api/gallery/content/:imagePath(*\.(jpg|bmp|png|gif|jpeg))"],
AuthenticationMWs.authenticate,
GalleryMWs.loadImage,
RenderingMWs.renderFile
);
};
private addGetImageThumbnail() {
this.app.get("/api/gallery/content/:imagePath(*\.(jpg|bmp|png|gif|jpeg))/thumbnail/:size?",
private static addGetImageThumbnail(app) {
app.get("/api/gallery/content/:imagePath(*\.(jpg|bmp|png|gif|jpeg))/thumbnail/:size?",
AuthenticationMWs.authenticate,
GalleryMWs.loadImage,
ThumbnailGeneratorMWs.generateThumbnail,
@@ -44,8 +44,8 @@ export class GalleryRouter {
);
};
private addGetImageIcon() {
this.app.get("/api/gallery/content/:imagePath(*\.(jpg|bmp|png|gif|jpeg))/icon",
private static addGetImageIcon(app) {
app.get("/api/gallery/content/:imagePath(*\.(jpg|bmp|png|gif|jpeg))/icon",
AuthenticationMWs.authenticate,
GalleryMWs.loadImage,
ThumbnailGeneratorMWs.generateIcon,
@@ -53,8 +53,8 @@ export class GalleryRouter {
);
};
private addSearch() {
this.app.get("/api/search/:text",
private static addSearch(app) {
app.get("/api/search/:text",
AuthenticationMWs.authenticate,
GalleryMWs.search,
ThumbnailGeneratorMWs.addThumbnailInformation,
@@ -63,8 +63,8 @@ export class GalleryRouter {
);
};
private addInstantSearch() {
this.app.get("/api/instant-search/:text",
private static addInstantSearch(app) {
app.get("/api/instant-search/:text",
AuthenticationMWs.authenticate,
GalleryMWs.instantSearch,
ThumbnailGeneratorMWs.addThumbnailInformation,
@@ -73,8 +73,8 @@ export class GalleryRouter {
);
};
private addAutoComplete() {
this.app.get("/api/autocomplete/:text",
private static addAutoComplete(app) {
app.get("/api/autocomplete/:text",
AuthenticationMWs.authenticate,
GalleryMWs.autocomplete,
RenderingMWs.renderResult

View File

@@ -2,11 +2,12 @@ import * as _express from "express";
import {NextFunction, Request, Response} from "express";
import * as _path from "path";
import {Utils} from "../../common/Utils";
import {Config} from "../config/Config";
import {Config} from "../../common/config/private/Config";
export class PublicRouter {
constructor(private app) {
this.app.use((req:Request, res:Response, next:NextFunction) => {
public static route(app) {
app.use((req: Request, res: Response, next: NextFunction) => {
res.tpl = {};
res.tpl.user = null;
@@ -20,15 +21,15 @@ export class PublicRouter {
return next();
});
this.app.use(_express.static(_path.resolve(__dirname, './../../frontend')));
this.app.use('/node_modules', _express.static(_path.resolve(__dirname, './../../node_modules')));
this.app.use('/common', _express.static(_path.resolve(__dirname, './../../common')));
app.use(_express.static(_path.resolve(__dirname, './../../frontend')));
app.use('/node_modules', _express.static(_path.resolve(__dirname, './../../node_modules')));
app.use('/common', _express.static(_path.resolve(__dirname, './../../common')));
const renderIndex = (req: Request, res: Response) => {
res.render(_path.resolve(__dirname, './../../frontend/index.ejs'), res.tpl);
};
this.app.get(['/', '/login', "/gallery*", "/admin", "/search*"], renderIndex);
app.get(['/', '/login', "/gallery*", "/admin", "/search*"], renderIndex);
}

View File

@@ -2,22 +2,22 @@ import {AuthenticationMWs} from "../middlewares/user/AuthenticationMWs";
import {UserRoles} from "../../common/entities/UserDTO";
export class SharingRouter {
constructor(private app: any) {
public static route(app: any) {
this.addGetSharing();
this.addUpdateSharing();
this.addGetSharing(app);
this.addUpdateSharing(app);
}
private addGetSharing() {
this.app.get("/api/share/:directory",
private static addGetSharing(app) {
app.get("/api/share/:directory",
AuthenticationMWs.authenticate,
AuthenticationMWs.authorise(UserRoles.User)
//TODO: implement
);
};
private addUpdateSharing() {
this.app.post("/api/share/:directory",
private static addUpdateSharing(app) {
app.post("/api/share/:directory",
AuthenticationMWs.authenticate,
AuthenticationMWs.authorise(UserRoles.User)
//TODO: implement

View File

@@ -5,29 +5,29 @@ import {UserRequestConstrainsMWs} from "../middlewares/user/UserRequestConstrain
import {RenderingMWs} from "../middlewares/RenderingMWs";
export class UserRouter {
constructor(private app) {
this.addLogin();
this.addLogout();
this.addGetSessionUser();
this.addChangePassword();
public static route(app) {
this.addLogin(app);
this.addLogout(app);
this.addGetSessionUser(app);
this.addChangePassword(app);
this.addCreateUser();
this.addDeleteUser();
this.addListUsers();
this.addChangeRole();
this.addCreateUser(app);
this.addDeleteUser(app);
this.addListUsers(app);
this.addChangeRole(app);
}
private addLogin() {
this.app.post("/api/user/login",
private static addLogin(app) {
app.post("/api/user/login",
AuthenticationMWs.inverseAuthenticate,
AuthenticationMWs.login,
RenderingMWs.renderSessionUser
);
};
private addLogout() {
this.app.post("/api/user/logout",
private static addLogout(app) {
app.post("/api/user/logout",
AuthenticationMWs.authenticate,
AuthenticationMWs.logout,
RenderingMWs.renderOK
@@ -35,16 +35,16 @@ export class UserRouter {
};
private addGetSessionUser() {
this.app.get("/api/user/login",
private static addGetSessionUser(app) {
app.get("/api/user/login",
AuthenticationMWs.authenticate,
RenderingMWs.renderSessionUser
);
};
private addChangePassword() {
this.app.post("/api/user/:id/password",
private static addChangePassword(app) {
app.post("/api/user/:id/password",
AuthenticationMWs.authenticate,
UserRequestConstrainsMWs.forceSelfRequest,
UserMWs.changePassword,
@@ -53,8 +53,8 @@ export class UserRouter {
};
private addCreateUser() {
this.app.put("/api/user",
private static addCreateUser(app) {
app.put("/api/user",
AuthenticationMWs.authenticate,
AuthenticationMWs.authorise(UserRoles.Admin),
UserMWs.createUser,
@@ -62,8 +62,8 @@ export class UserRouter {
);
};
private addDeleteUser() {
this.app.delete("/api/user/:id",
private static addDeleteUser(app) {
app.delete("/api/user/:id",
AuthenticationMWs.authenticate,
AuthenticationMWs.authorise(UserRoles.Admin),
UserRequestConstrainsMWs.notSelfRequest,
@@ -73,8 +73,8 @@ export class UserRouter {
};
private addListUsers() {
this.app.get("/api/user/list",
private static addListUsers(app) {
app.get("/api/user/list",
AuthenticationMWs.authenticate,
AuthenticationMWs.authorise(UserRoles.Admin),
UserMWs.listUsers,
@@ -82,8 +82,8 @@ export class UserRouter {
);
};
private addChangeRole() {
this.app.post("/api/user/:id/role",
private static addChangeRole(app) {
app.post("/api/user/:id/role",
AuthenticationMWs.authenticate,
AuthenticationMWs.authorise(UserRoles.Admin),
UserRequestConstrainsMWs.notSelfRequestOr2Admins,

View File

@@ -1,18 +1,19 @@
import * as _express from "express";
import * as _session from "express-session";
import * as _bodyParser from "body-parser";
import * as _debug from "debug";
import * as _http from "http";
import * as winston from "winston";
import * as expressWinston from "express-winston";
import {PublicRouter} from "./routes/PublicRouter";
import {UserRouter} from "./routes/UserRouter";
import {GalleryRouter} from "./routes/GalleryRouter";
import {AdminRouter} from "./routes/AdminRouter";
import {ErrorRouter} from "./routes/ErrorRouter";
import {SharingRouter} from "./routes/SharingRouter";
import {DatabaseType} from "../common/config/Config";
import {ObjectManagerRepository} from "./model/ObjectManagerRepository";
import {Config} from "./config/Config";
import {Logger} from "./Logger";
import {Config} from "../common/config/private/Config";
import {DatabaseType} from "../common/config/private/IPrivateConfig";
const LOG_TAG = "[server]";
export class Server {
@@ -22,18 +23,46 @@ export class Server {
private server: any;
constructor() {
this.init();
}
async init() {
Logger.info(LOG_TAG, "config:");
Logger.info(LOG_TAG, JSON.stringify(Config, null, '\t'));
this.debug = _debug("PiGallery2:server");
this.app = _express();
this.app.set('view engine', 'ejs');
this.app.use(expressWinston.logger({
transports: [
new winston.transports.Console({
level: 'silly',
json: false,
colorize: true,
timestamp: function () {
return (new Date()).toLocaleString();
},
formatter: (options) => {
// Return string will be passed to logger.
return options.timestamp() + '[' + winston['config']['colorize'](options.level, options.level.toUpperCase()) + '] ' +
(undefined !== options.message ? options.message : '') +
(options.meta && Object.keys(options.meta).length ? '\n\t' + JSON.stringify(options.meta) : '' );
},
debugStdout: true
})
],
meta: false, // optional: control whether you want to log the meta data about the request (default to true)
msg: "HTTP {{req.method}} {{req.url}}", // optional: customize the default logging message. E.g. "{{res.statusCode}} {{req.method}} {{res.responseTime}}ms {{req.url}}"
expressFormat: true, // Use the default Express/morgan request formatting. Enabling this will override any msg if true. Will only output colors with colorize set to true
colorize: true, // Color the text and status code, using the Express/morgan color palette (text: gray, status: default green, 3XX cyan, 4XX yellow, 5XX red).
level: (req) => {
if (req.url.indexOf("/api/") !== -1) {
return "verbose";
}
return req.url.indexOf("node_modules") !== -1 ? "silly" : "debug"
}
}));
if (process.env.DEBUG) {
let _morgan = require('morgan');
this.app.use(_morgan('dev'));
}
this.app.set('view engine', 'ejs');
/**
@@ -57,19 +86,26 @@ export class Server {
this.app.use(_bodyParser.json());
if (Config.Server.database.type == DatabaseType.mysql) {
ObjectManagerRepository.InitMySQLManagers().catch((err) => {
Logger.warn(LOG_TAG, "Error during initailizing mysql falling back to memory DB", err);
try {
await ObjectManagerRepository.InitMySQLManagers();
} catch (err) {
Logger.warn(LOG_TAG, "[MYSQL error]", err);
Logger.warn(LOG_TAG, "Error during initializing mysql falling back to memory DB");
Config.setDatabaseType(DatabaseType.memory);
ObjectManagerRepository.InitMemoryManagers();
});
await ObjectManagerRepository.InitMemoryManagers();
}
} else {
ObjectManagerRepository.InitMemoryManagers();
await ObjectManagerRepository.InitMemoryManagers();
}
if (Config.Server.thumbnail.hardwareAcceleration == true) {
try {
const sharp = require.resolve("sharp");
const sharp = require("sharp");
sharp();
} catch (err) {
Logger.warn(LOG_TAG, "[Thumbnail hardware acceleration] sharp module error: ", err);
Logger.warn(LOG_TAG, "Thumbnail hardware acceleration is not possible." +
" 'Sharp' node module is not found." +
" Falling back to JS based thumbnail generation");
@@ -77,14 +113,14 @@ export class Server {
}
}
new PublicRouter(this.app);
PublicRouter.route(this.app);
new UserRouter(this.app);
new GalleryRouter(this.app);
new SharingRouter(this.app);
new AdminRouter(this.app);
UserRouter.route(this.app);
GalleryRouter.route(this.app);
SharingRouter.route(this.app);
AdminRouter.route(this.app);
new ErrorRouter(this.app);
ErrorRouter.route(this.app);
// Get PORT from environment and store in Express.
@@ -138,7 +174,7 @@ export class Server {
const bind = typeof addr === 'string'
? 'pipe ' + addr
: 'port ' + addr.port;
Logger.debug(LOG_TAG, 'Listening on ' + bind);
Logger.info(LOG_TAG, 'Listening on ' + bind);
};
}