18

I am working on an application that will run on OSX and windows. I want the logs to be written to the users home directory. For OSX it will be under the /Users//Library/Application Support/MyApp/log directory, and under windows depending on the version under /Users//AppData/MyApp/log directory.

What is the best way I can do this? I have looked around for solutions for this, but nothing helpful or a solution I am comfortable using has come up.

Look forward to your inputs.

edit: Since the location of the log file depends on the OS, I am hoping to find a run time solution, possibly something like below

if (System.getProperty("os.name").contains("mac"))
    logFileLocation = System.getProperty("user.home") + "/Library/Application Support/MyApp/logs"
else
    logFileLocation = System.getenv("APPDATA") + "/MyApp/logs"

Thanks

Poorav
  • 415
  • 1
  • 5
  • 14

4 Answers4

30

Change the ConsoleAppender to a FileAppender. As far as I know the write request will be redirected to appdata on windows OS. Not sure about MacOs.

URL mySource = MyAppMainClass.class.getProtectionDomain().getCodeSource().getLocation();
File rootFolder = new File(mySource.getPath());
System.setProperty("app.root", rootFolder.getAbsolutePath());

and edit log4j config like this

log4j.appender.NotConsole=org.apache.log4j.RollingFileAppender
log4j.appender.NotConsole.fileName=${app.root}/fileName.log

or for user home:

// log4j 1.*
log4j.appender.NotConsole.fileName=${user.home}/fileName.log
// log4j 2.*
log4j.appender.NotConsole.fileName=${sys:user.home}/fileName.log

Note that log4j 2 requires sys: prefix - thanks to @sgrubsmyon

Observer
  • 710
  • 10
  • 14
  • note that you need to make sure you have the proper authorization on the folders. – Tomer Jul 26 '12 at 12:17
  • ${user.home} folder usually is open for write operations. It is the purpose of that folder to store user-specific files created upon session. –  Jul 26 '12 at 12:29
  • Thanks for the response, however in this solution the location of the log file will be the same regardless of the OS. Please see my edit above for further clarification on the problem. – Poorav Jul 26 '12 at 13:53
  • it will not ofc. {user.home} is a system variable so the paths will be different. Sorry I can't understand what do you want to have. – Observer Jul 26 '12 at 15:44
  • On mac, the user.home returns /Users/Me, on XP it will return c:\Documents and Settings\Me, on Win 7 it will return c:\Users\Me...that's fine. But the log file needs to be in.. OSX --> /Users/Me/Library/Application Support/MyApp/logs, for Win 7 c:\Users\Me\AppData\MyApp\logs, for XP c:\Docs&Sett\Me\Application Data\MyApp\logs. I hope that clarifies the issue more. – Poorav Jul 26 '12 at 20:37
  • 3
    With log4j2, one needs to use `${sys:user.home}`, see https://logging.apache.org/log4j/2.x/manual/configuration.html#PropertySubstitution – sgrubsmyon Aug 26 '20 at 15:37
3

Thank you all for the inputs. based on the hint that Alex proposed I went with the following approach,

In log4j.properties I had the following configuration

log4j.appender.FILE=org.apache.log4j.RollingFileAppender
log4j.appender.FILE.File=${userApp.root}/logs/myapp.log

and at the start of the application I have done this.

System.setProperty("userApp.root", getUserAppDirectory());

the getUserAppDirectory() method is defined as

static String getUserAppDirectory() {
    if (isMacOS())
        return System.getProperty("user.home") + "/Library/Application Support/myapp";
    else
        return System.getenv("APPDATA") + "/myapp";
}
Poorav
  • 415
  • 1
  • 5
  • 14
0

Possibly the cleanest approach would be to write your log4j configuration assuming a particular system property (say myapp.data.dir)

log4j.appender.logfile.fileName=${myapp.data.dir}/logs/myapp.log

and set that property in the appropriate way in the launcher for each platform. For example if you're using an .app bundle on Mac OS X then you can set system properties in Info.plist

<plist version="1.0">
<dict>
    <!-- ... -->
    <key>Java</key>
    <dict>
        <!-- ... -->
        <key>Properties</key>
        <dict>
            <key>apple.laf.useScreenMenuBar</key>
            <string>true</string>
            <key>myapp.data.dir</key>
            <string>$USER_HOME/Library/Application Support/MyApp</string>

or set it relative to the APPDATA environment variable on Windows (which would handle the difference between XP and 7). With a Launch4J .exe you could put -Dmyapp.data.dir="%APPDATA%\MyApp" in the .l4j.ini file.

You'd need explicit code to initialize log4j before you try and access any loggers

if(System.getProperty("myapp.data.dir") == null) {
  // fallback to ~/.myapp (sensible Linux default) if run without a launcher
  System.setProperty("myapp.data.dir", new File(
        System.getProperty("user.home"), ".myapp").getAbsolutePath());
}
// ensure log directory exists
new File(new File(System.getProperty("myapp.data.dir")), "logs").mkdirs();
// now it's safe to configure log4j
PropertyConfigurator.configure(this.getClass().getResource("/log4j.properties"));
Ian Roberts
  • 120,891
  • 16
  • 170
  • 183
0

I have Solved path issue of log4j.xml in mac :

in windows we configure log4j in web.xml like :

    <listener>
        <listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>
    </listener>

    <context-param> 
        <param-name>log4jConfigLocation</param-name> 
        <param-value>file:${LOG4J_HOME}/conf/log4j.xml</param-value> 

<!--   Above path is that where we have stored log4j.xml file externally  -->
    </context-param> 

    <context-param>
        <param-name>log4jRefreshInterval</param-name>
        <param-value>6000</param-value>
    </context-param>

Where ${LOG4J_HOME} is user varible we use to set in window. like

user variable = LOG4J_HOME value = D:/LOG4J (in d drive we have created a folder of name Log4J we copied that path and gave as a value)

In mac we have envirenvirent variable set fasility by bash command, but it didint work anymore.

so for mac we have to create one folder any where and we have to give static path of that folder.

like in xml :

<listener>
        <listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>
    </listener>

    <context-param> 
        <param-name>log4jConfigLocation</param-name> 
        <param-value>file:/Users/vardhaman/Desktop/LOG4J/conf/log4j.xml</param-value> 
<!--   Above path is that where we have stored log4j.xml file externally  to get this path go up to the log4j.xml file in external device and right click select get info, where we will get path, copy that path -->

    </context-param> 

    <context-param>
        <param-name>log4jRefreshInterval</param-name>
        <param-value>6000</param-value>
    </context-param>

Same way we have to do in log4j.xml file

In window we use to do like :

<appender name="CLICK-SPRING" class="org.apache.log4j.RollingFileAppender">
        <param name="File" value="${LOG4J_HOME}/logs/CLICK/CLICK-spring.log"/>
        <param name="Append" value="true"/>
        <param name="Threshold" value="DEBUG"/>
        <param name="MaxFileSize" value="100MB"/>
        <param name="MaxBackupIndex" value="10" />
        <layout class="org.apache.log4j.PatternLayout">
          <param name="ConversionPattern" value="%d{yyyy-MM-dd HH:mm:ss} %p [%t] %C{1}.%M(%L) | %m%n"/>
        </layout>
    </appender> 

In mac :

in place of value we have to copy static path up to folder LOG4J, or you can create any folder.

<appender name="CLICK-SPRING" class="org.apache.log4j.RollingFileAppender">
        <param name="File" value="Users/vardhaman/Desktop/LOG4J/logs/CLICK/CLICK-spring.log"/>
        <param name="Append" value="true"/>
        <param name="Threshold" value="DEBUG"/>
        <param name="MaxFileSize" value="100MB"/>
        <param name="MaxBackupIndex" value="10" />
        <layout class="org.apache.log4j.PatternLayout">
          <param name="ConversionPattern" value="%d{yyyy-MM-dd HH:mm:ss} %p [%t] %C{1}.%M(%L) | %m%n"/>
        </layout>
    </appender> 
vardhaman
  • 15
  • 3