How do I subclass/override Ember.Logger? - ember.js

I am implementing remote logging ability in my Ember app, where I want to push everything that gets sent to the log console to a remote logging service (e.g. Loggly).
I believe that what I need to do is override Ember.Logger's methods to redirect log output to the remote logging service, but I can't figure out how to do that.
The documentation for Ember.Logger simply states:
Override this to provide more robust logging functionality.
How do I "override this"? I've tried doing Ember.Logger.reopenClass() and it complains with Ember.Logger.reopenClass is not a function.
Where would I do this? In an initializer? In a service? Other?

Ember.Logger is not an Ember class. It's just an object with some methods on it.
You can override it by something like
Ember.Logger.log = function(...
You can put this wherever you want. I might put it at the top of app.js.

Expanding upon and updating #user663031 response...
As of Nov 2017, the status of Ember.Logger is up in the air. It was not included in Ember's module API, and there isn't yet an RFC for the future.
It is possible use a debug utility directly, e.g. ember-debug-logger, and extend those prototypes separate from Ember.Logger.
However, I opted to overwrite Ember.Logger directly because it allows me to include any logging tool that I like (as opposed to debug util) without having to modify the log statements scattered throughout the code.
As I use bunyan on the backend, opted to log with browser-bunyan, which incidentally has the same info, warn, error as Ember.Logger.
YMMV, but this is the minimal example that worked for me...
// app/app.js
import LOG from './logger-bunyan';
if (config.APP.LOG_BUNYAN) {
Ember.Logger = LOG;
}
// app/logger-bunyan.js
import bunyan from 'npm:browser-bunyan';
const LOG = bunyan.createLogger({
name: 'emberApplication',
});
export default LOG;
// config/environment.js
if (environment === 'development') {
ENV.APP.LOG_BUNYAN = true;
}
// app/component/WhereIWantToLog.js
Logger.warn('bunyan logged warning message')

Related

Python logging adds additional handlers

I'm trying to get to grips with Python's logging module which frankly so far has not been approachable. Currently I have one 'main' logger in my main script:
logger = logging.getLogger(__name__)
handler = logging.FileHandler('debug.log')
handler.setFormatter(logging.Formatter('%(levelname)s: %(asctime)s: %(name)s: %(message)s'))
logger.addHandler(handler)
logger.setLevel(logging.DEBUG)
logger.debug(
'{} run for {} using {} values.'.format(
skill, str(datetime.now()), key, mode
)
)
and I have a secondary logger in an imported module:
logger = logging.getLogger(__name__)
handler = logging.FileHandler('debug.log')
handler.setFormatter(logging.Formatter('%(levelname)s: %(asctime)s: %(name)s: %(message)s'))
logger.addHandler(handler)
However, although I tell both loggers to log to a file only (both only have the handlers I've set), I still get information printed to stout from the root logger. Calling logging.root.handlers shows the logger has a StreamHandler which only appears when importing the module containing the second module.
My hacking method of solving the additional stream is to just delete from the roots handlers. However, this feels like a non-canonical solution. I'm assuming I've implemented the module wrong in some way rather than this being the intended function of the module. How are you meant to set up loggers in this hierarchical fashion correctly?
A proper [mcve] would certainly help here - I can't reproduce this root logger handler suddenly appearing out of the blue.
This being said, you're doing it wrong anyway: one of the main goals of the logging module and which is not clearly, explicitely documented is to separate logger's usage (.getLogger() and logger.log()) calls from logging configuration.
The point is that library code cannot know in which context it will be used - only the application will -, so library code should NOT try to configure their loggers in anyway - just get a logger and use it, period. Then it's up to the application (here your main.py script) to configure the loggers for the libs (hint: the .dictConfig() function is by far the most usable way to configure eveything at once).

Swashbuckle (non-Core) configuration modification at runtime

Using the non-Core version of Swashbuckle (https://github.com/domaindrivendev/Swashbuckle), is there a way to modify the configuration after the application has launched? I cannot find a way to do this out of the box.
As an example, let's say I want to modify this at some point while the application is running:
.EnableSwaggerUi(c =>
{
c.SupportedSubmitMethods("GET");
});
Is this possible without modifying Swashbuckle itself?
Look into IDocumentFilters they get executed at runtime.
I have a few examples here:
SwashbuckleTest/blob/master/Swagger_Test/App_Start/SwaggerConfig.cs
And that EnableSwaggerUi( c => SupportedSubmitMethods ) is something that happens on the browser client side, you can change that behavior with a custom JS file, look in the config for InjectJavaScript.
You can also overwrite the default assets used in the swagger-ui (such as the index.html) with your own version, look in the config for CustomAsset

ember serve and browser reload results in "cannot GET /foo" with custom Ember.Location

TL;DR: Added custom location type to environment.js then ember serve -> open browser to route /foo -> cannot GET /foo
Followed the instructions at https://www.emberjs.com/api/classes/Ember.Location.html#toc_custom-implementation and copied the code exactly as it appeared into a file called app/locations/history-url-logging.js, added a line to config/environment.js that said:
ENV.locationType = 'history-url-logging';
For reference, the code given in the docs is simply:
import Ember from 'ember';
export default Ember.HistoryLocation.extend({
implementation: 'history-url-logging',
pushState: function (path) {
console.log(path);
this._super.apply(this, arguments);
}
});
I decided to restart the server, did the usual CTRL+C to ember s then did ember s again. I went back to my browser sitting on one of the routes, hit F5, and received the cryptic error:
Cannot GET /contacts
So, after MUCH Googling and trial and error (and posting a previous question here which I just edited with this text you're reading), I discovered that to FIX that error, all I had to do remove the config line ENV.locationType = 'history-url-logging';, restart the server (ember s), and suddenly the app worked fine!
What's even more odd is that if I start the app without that line in environment.js, then once the app is running (and the browser window reloads just fine, etc), then I re-add the line that says ENV.locationType = 'history-url-logging'; (which triggers a live reload), and the app still works fine! (E.g. hitting F5 to reload the page doesn't vie me the "Cannot GET /contacts" (or whatever the route is) error.) And, of course, the console gives me the "console.log" output as expected from the code above.
So, long and short of it, using a custom location totally seems to screw up ember serve - which is really sad and frustrating! Any ideas how to fix this?
Ember built-in server looks at the environment.js locationType property to figure out if it must serve routes after the rootURL path. By default, if the locationType is history it will do it. It uses string matching.
In your case you wrote your own location, inheriting from HistoryLocation therefor the locationType property in the environement.js is now history-url-logging. The built-in server doesn't recognize it as a history based form of location just by the name. It will default to hash location. It doesn't analyze your code.
For this scenario, we have to help the built-in server to understand that the locationType is equivalent to a history location.
You need to add historySupportMiddleware: true in your environment.js file right after the locationType property.

AWS Lambda NodeJS locale/variable isolation

There is a concern about potential problem with reusable variables in aws-lambda.
A user's locale is passed as
Browser cookies => AWS API Gateway => Lambda (NodeJS 6.10)
On the server side localization is implemented with a static variable in a class. Presenting typescript code for clarity but can be done in pure ECMAScript.
Module Language.ts
export default class Language
{
public static Current: LanguageCode = LanguageCode.es;
}
Static Language.Current variable is used across different parts of the application for manual localization and it works perfectly on the client side (react + redux).
Lambda function
import {APIGatewayEvent, Context, Callback} from 'aws-lambda';
import Language from './pathToModule/Language.ts';
export const api = function(event: APIGatewayEvent, context: Context, callback: Callback)
{
Language.Current = event.headers.cookie.locale;
// do the logic here
}
Potential problem
According to AWS documentation NodeJS instances can be reused for different requests. It means that famous concurrent problems have to be considered, e.g.
User 1 calls lambda function. The locale is set to English.
In parallel user 2 calls the same lambda instance. The local is changed to Spanish.
User 1 code continues and reads modified (wrong) locale variable from the shared module Language.
How do you resolve this problem?
For convenience it is good to have only one place for locale change. As I understand the same concern exists for all famous i18n npm packages (i18next, i18n, yahoo i18n, etc).
One of the best practices for Lambda functions is to try and not write code which maintains state.
Here you are initializing the locale based on an initial request and applying it to all future requests, which is inherently flawed even on server based code, forget server less.
To fix this, you will need to initialize the localization library for each request, or at least maintain an in memory lazy map, which you can make use of use the current request's locale to achieve the desired localization.
There are several solutions:
Node JS container is reused only after a function process is finished (callback or error is occurred) (thanks to #idbehold). Thus there is always a unique context per a function call.
Refactor code and pass a locale variable back and force (#Yeshodhan Kulkarni suggestion).
For example, return a function as an intermediate result and use it before calling the result back.
var localizableResult = ...;
var result = localizableResult.Localize(requestedLocale).
If there is a need to use a local stack (kind of a thread context) for other projects there is a npm package node-continuation-local-storage.
Case 1 makes it really simple to use global variables for current locale.

How to enable DEBUG level logging with Jetty embedded?

I'm trying to set the logging level to DEBUG in an embedded Jetty instance.
The documentation at http://docs.codehaus.org/display/JETTY/Debugging says to -
call SystemProperty.set("DEBUG", "true") before calling new
org.mortbay.jetty.Server().
I'm not sure what the SystemProperty class is, it doesn't seem to be documented anywhere. I tried System.setProperty(), but that didn't do the trick.
My question was answered on the Jetty mailing list by Joakim Erdfelt:
You are looking at the old Jetty 6.x docs at docs.codehaus.org.
DEBUG logging is just a logging level determined by the logging
implementation you choose to use.
If you use slf4j, then use slf4j's docs for configuring logging level. http://slf4j.org/manual.html
If you use java.util.logging, use the JVM docs. http://docs.oracle.com/javase/6/docs/technotes/guides/logging/overview.html
If you use the built-in StdErrLog, then there is a pattern to follow.
-D{classref}.LEVEL={level}
Where {classref} is the class reference you want to set the level on,
and all sub-class refs. and {level} is one of the values ALL, DEBUG,
INFO, WARN
Example:
-Dorg.eclipse.jetty.LEVEL=INFO - this will enable INFO level logging for all jetty packages / classes.
-Dorg.eclipse.jetty.io.LEVEL=DEBUG - this will enable DEBUG level logging for IO classes only
-Dorg.eclipse.jetty.servlet.LEVEL=ALL - this will enable ALL logging (trace events, internally ignored exceptions, etc..) for servlet
packages.
-Dorg.eclipse.jetty.util.thread.QueuedThreadPool.LEVEL=ALL - this will enable level ALL+ on the specific class only.
Add this
-Dorg.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StdErrLog
-Dorg.eclipse.jetty.LEVEL=DEBUG
In case you just want to quickly get log messages to stderr add something like this to java command line:
-Dorg.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StdErrLog -D{classref}.LEVEL=DEBUG
You can use this snippet to enable logging:
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.StdErrLog;
.
.
.
StdErrLog logger = new StdErrLog();
logger.setDebugEnabled(true);
Log.setLog(logger);