How to create predictable logging locations with sbt-native-packager - sbt-native-packager

I am using sbt-native-packager with the experimental Java Server archetype. I am trying to identify a conventional way to access my log files, and I'm wondering if anyone knows of a common approach here. Since I am using the Java Server archetype, I am getting a symlink /var/log/$app -> install_dir/$app/log, but it feels a little dirty and less portable to just have log4j open /var/log/$app/error.log directly.
[Update]
I ended up creating an object with run time path information:
object MakaraPaths {
def getLogPath = new File(getJarPath, "../logs").getPath
def getConfigPath = new File(getJarPath, "../conf").getPath
def getJarPath = {
val regex = new Regex("(/.+/)*")
val jarPath = Makara.getClass.getProtectionDomain.getCodeSource.getLocation.getPath
(regex findAllIn jarPath).mkString("")
}
}
In my main method, I established a system property based on the new MakaraPaths object:
System.setProperty("logPath", MakaraPaths.getLogPath)
I also used this for my config file:
val config = ConfigFactory.parseFile(new File(MakaraPaths.getConfigPath, "application.conf"))
Ultimately, to load the log file, I used a System Property lookup:
<RollingFile name="fileAppender" fileName="${sys:logPath}/server.log" filePattern="${sys:logPath}/server_%d{yyMMdd}.log">
This gets me most of the way where I needed to be. It's not completely portable, but it does technically support my use case. (Deploying to Ubuntu)

You could use relative path in log4j configuration. Just write logs in logs/filename.log.
During installation symlink install_dir/$app/logs -> /var/log/$app will be created, and all logs will be written in /var/log/$app/filename.log

Related

MapUtils with Logger

I am using MapUtils.verbosePrint(System.out, "", map) to dump the contents of a map in Java. They (management) do not like us using System.out.println().
We are using log4j. They made the logger into a variable "l" so we can say something like l.debug("This is going to the logfile in debug mode).
I would like to get the output buffer(s) from l so I could pass it into verbosePrint() instead of System.out. I looked at all the methods and members of the logger and did things like getAppenders() and tried all those elements but I could not find anything that helped.
Has anyone else done this? I know the logger may write to > 1 output.
You can use Log4j IOStreams to create PrintStreams that will send everything to a logger. This is mostly useful to log debug output from legacy APIs like JDBC or Java Mail that do not have a proper logging system. I wouldn't advise it in other cases, since your messages might be merged or split into several log messages.
I would rather use one of these approaches:
simply log the map using Logger#debug(Object). This will lazily create an ObjectMessage (only if debug is enabled), which is usually formatted using the map's toString() method. Some layouts might format it differently (like the JSON Template Layout).
eagerly create a MapMessage or StringMapMessage:
if (l.isDebugEnabled()) {
l.debug(new MapMessage(map));
}
This gives you more formatting options. For example the layout pattern %m{JSON} will format your message as JSON.
if your are set on the format provided by MapUtils#verbosePrint, you can extend ObjectMessage and overwrite its getFormattedMessage() and formatTo() methods.
public String getFormattedMessage() {
final ByteArrayOutputStream os = new ByteArrayOutputStream();
MapUtils.verbosePrint(new PrintStream(os), "", );
return new String(os.toByteArray());
}

How do I print the current NixOS' nix.package version?

A NixOS configuration is built using the /etc/nixos/configuration.nix file. This configuration has a nix.package property.
In an NixOS instance, I want to print the version/hash (i.e., unique identifier) of the nix.package object that has been used in building the current instance. Ideally, this should be stored inside a lockfile, but I don't believe the current version of nixos-rebuild uses those.
Should this not be possible, can I explicitly store this hash somewhere during the build process by modifying my /etc/nixos/configuration.nix?
Yes, you can access this attribute via NixOS' config parameter and use it in your configuration, or as part of a package.
For example, this module causes the version and the store path to be written to files in /etc upon activation.
{ config, lib, ... }:
{
config = {
environment.etc."x-nix-version".text =
config.nix.package.version;
environment.etc."x-nix-path".text =
"${config.nix.package}";
};
}
Alternatively, you can extract it from a potentially not yet built configuration using the nixos-option command or nix repl '<nixpkgs/nixos>'.

Writing ENV variables to configure an npm module

I currently have a project in a loose ES6 module format and my database connection is hard coded. I am wanting to turn this into an npm module and am now facing the issue of how to best allow the end user to configure the code. My first attempt was to rewrite it as classes to be instantiated but it is making the use of the code more convoluted than before so am looking at alternatives. I am exploring my configuration options. It looks like writing to the process env would be the way but I am pondering potential issues, no-nos and other options I have not considered.
Is having the user write config to process env an acceptable method of configuring an npm module? It's a bit like a global write so am dealing with namespace considerations for one. I have also considered using package.json but that's not going to work for things like credentials. Likewise using an rc file is cumbersome. I have not found any docs on the proper methodology if any.
process.env['MY_COOL_MODULE_DB'] = ...
There are basically 5ish options as I see it:
hardcode - not an option
create a configured scope such as classes - what I have now and bleh
use a config such as node-config - not really a user friendly option for npm
store as globals/env. As suggested in comment I can wrap that process in an exported function and thereby ensure that I have a complex non collisive namespace while abstracting that from end user
Ask user to create some .rc file - I would if I was big time like AWS but not in this case.
I mention this npm use case but this really applies to the general challenge of configuring code that is exported as functions. I have use cases for classes but when the only need is creating a configured scope at the expense (in my case) of more complex code I am not sure its worth it.
Update I realize this is a bit of a discussion question but it's helped me wrap my brain around options. I think something like this:
// options.js
let options = {}
export function setOptions(o) { options = o }
export function getOptions(o) { return options }
Then have the user call setOptions() and call this getOptions internally. I realize that since Node requires the module just once that my options object will be kept configured as I pass it around.
NPM modules should IMO be agnostic as to where configuration is stored. That should be left up to the developer, and they may pick their favorite method (env vars, rc files, JSON files, whatever).
The configuration can be passed to your module in various ways. A common way is to export a function that takes an options object:
export default options => {
let db = database.connect(options.database);
...
}
From there, it really depends on what exactly your module provides. If it's just a bunch of loosely coupled functions, you can just return an object:
export default options => {
let db = database.connect(options.database);
return {
getUsers() { return db.getUsers() }
}
}
If you want to allow multiple versions of that object to exist simultaneously, you can use classes:
class MyClass {
constructor(options) {
...
}
...
}
export default options => {
return new MyClass(options)
}
Or export the entire class itself.
If the number of configuration options is limited (say 3 or less), you can also allow them to be passed as separate arguments, instead of passing an object.

How to log Process id using Log4cxx or log4j

I am using log4cxx my project and i can able to log current thread id using [%t] marker, how to log process id in it or log4j?.
I am using ConversionPattern & xml based configuration file.
Thanks,
Based on the above answers, I'm going to do this in log4j as follows:
import java.lang.management.*;
import org.apache.log4j.MDC;
private String getPID() {
RuntimeMXBean rt = ManagementFactory.getRuntimeMXBean();
return rt.getName();
}
private void configLog4j() {
// call this from somewhere before you start logging
MDC.put("PID", getPID());
}
Then in my log4j.properties:
log4j.appender.FILE.layout.ConversionPattern=%d %X{PID} [%t] %p %c{1} %x - %m%n
This will actually yield a PID that consists of the ID number and the hostname, at least on my implementation of Java, and from what I read that could be implementation specific. You could go further and split out just the PID.
I've grepped through log4j's and log4cxx's documentation and nowhere did I find anything about logging process id.
So, to be short: no, you can't log process id, at least not directly.
Since you're using C++, you can get your program's PID. Here is how to do it in Linux (Ubuntu in this link). Here is how do do it in Windows.
Get that PID at your program start, use an MDC and put your PID in it.
I don't think there's a better way.
Doing this in log4j would be even trickier, since I know of no way for a running program to get it's PID using standard Java classes.
This doesnt exist in any of the log4xxx, but with a litle effort you can make it yourself. Actually it's a very simple task if you don't mind a little coding. This is basically what I did few times - override actual appender, or it's layout, make sure your class sticks the process ID into event properties map. Then use this property by name as if it was an MDC property. Using MDC directly like suggested above is not the best choice because they are thread bound and you will have to make sure every thread puts the PID when it starts. But if you can't or don't want to override the appender or layout, then this would probably be the only option.
The answer by #skiphoppy works very well for Log4j1.x, but I thought it could be updated to show how it works in the new Log4j2.
(NOTE: I tried to submit this as an edit of the posting above as it is only a minor revision of the answer code, but I'm posting it as a separate answer since my revision was rejected.)
import java.lang.management.ManagementFactory;
import java.lang.management.RuntimeMXBean;
import org.apache.logging.log4j.ThreadContext;
private String getPID() {
RuntimeMXBean rt = ManagementFactory.getRuntimeMXBean();
return rt.getName();
}
private void configLog4j() {
// Here is the Log4j2 way
ThreadContext.put("PID", rtmx.getName());
}
As skiphoppy's answer states, it outputs a little more than just the process ID. For instance, on my machine (Fedora 20):
16237#localhost.localdomain
You can extract just the process id with the following code, placed in your XML configuration file: %replace{%X{PID}}{[A-Za-z#\.]*}{}
Given the output above for the process id:
16237#localhost.localdomain
the regex will produce
16237
There is no feature in Log4J to achieve this, however you could pass the process id in and use that.
This blog post shows one way to go about it: http://blackbeanbag.net/wp/2009/01/14/log-file-management-in-coherence-using-log4j/
Basically, pass in the process id as a system property and then use that in the Log4j pattern.
Apparently, this is due to the JVM not providing an easy method to access the process id. From JDK1.5+, this may work.
(Archived from dead link http://www.theresearchkitchen.com/archives/100 )

Using log4net as a logging mechanism for SSIS?

Does anyone know if it is possible to do logging in SSIS (SQL Server Integration Services) via log4net? If so, any pointers and pitfalls to be aware of? How's the deployment story?
I know the best solution to my problem is to not use SSIS. The reality is that as much as I hate this POS technology, the company I work with encourages the use of these apps instead of writing code. Meh.
So to answer my own question: it is possible. I'm not sure how our deployment story will be since this will be done in a few weeks from now.
I pretty much took the information from these sources and made it work. This one explains how to make referencing assemblies work with SSIS, click here. TLDR version: place it in the GAC and also copy the dll to the folder of your targetted framework. In my case, C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727. To programmatically configure log4net I ended up using this link as reference.
This is how my logger configuration code looks like for creating a file with the timestamp on it:
using log4net;
using log4net.Config;
using log4net.Layout;
using log4net.Appender;
public class whatever
{
private ILog logger;
public void InitLogger()
{
PatternLayout layout = new PatternLayout("%date [%level] - %message%newline");
FileAppender fileAppenderTrace = new FileAppender();
fileAppenderTrace.Layout = layout;
fileAppenderTrace.AppendToFile = false;
// Insert current date and time to file name
String dateTimeStr = DateTime.Now.ToString("yyyyddMM_hhmm");
fileAppenderTrace.File = string.Format("c:\\{0}{1}", dateTimeStr.Trim() ,".log");
// Configure filter to accept log messages of any level.
log4net.Filter.LevelMatchFilter traceFilter = new log4net.Filter.LevelMatchFilter();
traceFilter.LevelToMatch = log4net.Core.Level.All;
fileAppenderTrace.ClearFilters();
fileAppenderTrace.AddFilter(traceFilter);
fileAppenderTrace.ImmediateFlush = true;
fileAppenderTrace.ActivateOptions();
// Attach appender into hierarchy
log4net.Repository.Hierarchy.Logger root = ((log4net.Repository.Hierarchy.Hierarchy)LogManager.GetRepository()).Root;
root.AddAppender(fileAppenderTrace);
root.Repository.Configured = true;
logger = log4net.LogManager.GetLogger("root");
}
}
Hopefully this might help someone in the future or at least serve as a reference if I ever need to do this again.
Sorry, you didn't dig deep enough. There are 5 different destinations that you can log to, and 7 columns you can choose to include or not include in your logging as well as between 18 to 50 different events that you can capture logging on. You appear to have chosen the default logging, and dismissed it because it didn't work for you out of the box.
Check these two blogs for more information on what can be done with SSIS logging:
http://consultingblogs.emc.com/jamiethomson/archive/2005/06/11/SSIS_3A00_-Custom-Logging-Using-Event-Handlers.aspx
http://www.sqlservercentral.com/blogs/michael_coles/archive/2007/10/09/3012.aspx

Resources