Log4j, định cấu hình Ứng dụng web để sử dụng đường dẫn tương đối


80

Tôi có một ứng dụng web java phải được triển khai trên máy Win hoặc Linux. Bây giờ tôi muốn thêm log4j để ghi nhật ký và tôi muốn sử dụng một đường dẫn tương đối cho tệp nhật ký vì tôi không muốn thay đổi đường dẫn tệp trong mỗi lần triển khai. Vùng chứa rất có thể sẽ là Tomcat nhưng không nhất thiết.

Cách tốt nhất để làm điều này là gì?


3
Đây thực sự chỉ là một nửa câu hỏi. Có một đường dẫn động cho tệp nhật ký là rất tốt, nhưng còn tệp cấu hình thì sao. Nếu nó chỉ là vị trí tệp nhật ký động thì bạn sẽ có cùng mức nhật ký ở tất cả những nơi mà nó được triển khai và tôi không tưởng tượng điều đó là mong muốn. Tôi muốn biết phương pháp tốt nhất để chỉ định động cấu hình để môi trường nhà phát triển của tôi có thể đăng nhập tại DEBUG và sản xuất tại INFO / WARN. Bạn nghĩ sao?
Lucas

Câu trả lời:


100

Tomcat đặt thuộc tính hệ thống catalina.home. Bạn có thể sử dụng điều này trong tệp thuộc tính log4j của mình. Một cái gì đó như thế này:

log4j.rootCategory=DEBUG,errorfile

log4j.appender.errorfile.File=${catalina.home}/logs/LogFilename.log

Trên Debian (bao gồm cả Ubuntu), ${catalina.home}sẽ không hoạt động vì nó trỏ đến / usr / share / tomcat6 không có liên kết đến / var / log / tomcat6. Ở đây chỉ sử dụng ${catalina.base}.

Nếu bạn sử dụng một vùng chứa khác, hãy cố gắng tìm một thuộc tính hệ thống tương tự hoặc xác định thuộc tính của riêng bạn. Việc đặt thuộc tính hệ thống sẽ khác nhau tùy theo nền tảng và vùng chứa. Nhưng đối với Tomcat trên Linux / Unix, tôi sẽ tạo một setenv.sh trong thư mục CATALINA_HOME / bin. Nó sẽ chứa:

export JAVA_OPTS="-Dcustom.logging.root=/var/log/webapps"

Sau đó, các thuộc tính log4j.properties của bạn sẽ là:

log4j.rootCategory=DEBUG,errorfile

log4j.appender.errorfile.File=${custom.logging.root}/LogFilename.log

2
Thành thật mà nói, tôi không thấy phương pháp này có lợi thế gì so với việc sử dụng Trình xử lý mà tôi đã giải thích trong câu trả lời của mình. Tôi không quan tâm vùng chứa đó là gì, nó sẽ hoạt động cho dù tôi triển khai nó ở đâu trong khi trong cách tiếp cận của bạn, tôi phải thay đổi giá trị nếu tôi thay đổi môi trường.
Iker Jimenez

4
Cả hai giải pháp đều đang sử dụng thuộc tính hệ thống, chúng tôi chỉ thiết lập chúng khác nhau. Nó thực sự phụ thuộc vào sự linh hoạt và đơn giản. Chúng tôi quản lý tất cả các máy chủ Tomcat mà ứng dụng của chúng tôi chạy trên đó, vì vậy tôi thích sự linh hoạt. Nếu bạn phân phối một cuộc chiến tranh để sử dụng bên thứ 3, đơn giản làm cho tinh thần
Steve K

54

Cuối cùng tôi đã làm theo cách này.

Đã thêm một ServletContextListener thực hiện những việc sau:

public void contextInitialized(ServletContextEvent event) {
    ServletContext context = event.getServletContext();
    System.setProperty("rootPath", context.getRealPath("/"));
}

Sau đó, trong tệp log4j.properties:

log4j.appender.file.File=${rootPath}WEB-INF/logs/MyLog.log

Làm theo cách này, Log4j sẽ ghi vào đúng thư mục miễn là bạn không sử dụng nó trước khi thuộc tính hệ thống "rootPath" được đặt. Điều này có nghĩa là bạn không thể sử dụng nó từ chính ServletContextListener nhưng bạn có thể sử dụng nó từ bất kỳ nơi nào khác trong ứng dụng.

Nó sẽ hoạt động trên mọi vùng chứa web và hệ điều hành vì nó không phụ thuộc vào thuộc tính hệ thống cụ thể của vùng chứa và không bị ảnh hưởng bởi các vấn đề về đường dẫn cụ thể của hệ điều hành. Đã thử nghiệm với các vùng chứa web Tomcat và Orion cũng như trên Windows và Linux và nó hoạt động tốt cho đến nay.

Bạn nghĩ sao?


4
Đây là một ý tưởng hay, nhưng tôi nghĩ rằng catalina.home có thể an toàn hơn khi sử dụng vì nó sẽ luôn được đặt / có sẵn trước khi khởi chạy bất kỳ mã log4j nào.
matt b

6
Điều đó sẽ đúng nếu tôi chỉ sử dụng Tomcat, nhưng yêu cầu của tôi là nó phải hoạt động trên bất kỳ vùng chứa nào có cấu hình 0. Cách tiếp cận của tôi đáp ứng được điều này và cho đến nay không ai đề xuất một cách tiếp cận tốt hơn cho việc này.
Iker Jimenez

2
Giải pháp này có thể hoạt động cho các ứng dụng Web sử dụng Servlet, nhưng giải pháp của Steve K ( stackoverflow.com/questions/216781/… ) hoạt động cho bất kỳ ứng dụng nào sử dụng Log4j.
Derek Mahar

2
Giải pháp của Spencer K, cũng dựa trên các đường dẫn tương đối, hoạt động cho bất kỳ ứng dụng nào sử dụng Log4j, giả sử rằng bạn đặt thư mục cơ sở thành một đường dẫn có thể dự đoán được.
Derek Mahar

12
Giải pháp này chỉ hoạt động nếu bạn có một WebApp đơn được định cấu hình để sử dụng nó vì thuộc tính Hệ thống là chung cho tomcat một ứng dụng thứ hai khởi động sẽ ghi đè giá trị mà ứng dụng web đầu tiên đã đặt. Bạn có thể đặt cho mỗi ứng dụng web một tên thuộc tính duy nhất nhưng nếu bạn định làm điều đó thì bạn cũng có thể chỉ cần sử dụng $ {catalina.home} và thêm phần duy nhất của đường dẫn trong tệp log4j.properties vì ​​nó ít bị lỗi hơn .
3urdoch

14

Nếu bạn sử dụng Spring, bạn có thể:

1) tạo tệp cấu hình log4j, ví dụ: "/WEB-INF/classes/log4j-myapp.properties" KHÔNG đặt tên là "log4j.properties"

Thí dụ:

log4j.rootLogger=ERROR, stdout, rollingFile

log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - <%m>%n

log4j.appender.rollingFile=org.apache.log4j.RollingFileAppender
log4j.appender.rollingFile.File=${myWebapp-instance-root}/WEB-INF/logs/application.log
log4j.appender.rollingFile.MaxFileSize=512KB
log4j.appender.rollingFile.MaxBackupIndex=10
log4j.appender.rollingFile.layout=org.apache.log4j.PatternLayout
log4j.appender.rollingFile.layout.ConversionPattern=%d %p [%c] - %m%n
log4j.appender.rollingFile.Encoding=UTF-8

Chúng tôi sẽ định nghĩa "myWebapp-instance-root" sau tại điểm (3)

2) Chỉ định vị trí cấu hình trong web.xml:

<context-param>
  <param-name>log4jConfigLocation</param-name>
  <param-value>/WEB-INF/classes/log4j-myapp.properties</param-value>
</context-param>

3) Chỉ định một tên biến duy nhất cho gốc ứng dụng web của bạn, ví dụ: "myWebapp-instance-root"

<context-param>
  <param-name>webAppRootKey</param-name>
  <param-value>myWebapp-instance-root</param-value>
</context-param>

4) Thêm Log4jConfigListener:

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

Nếu bạn chọn một tên khác, hãy nhớ thay đổi nó trong log4j-myapp.properties.

Xem bài viết của tôi (chỉ tiếng Ý ... nhưng nó phải hiểu): http://www.megadix.it/content/configurare-path-relativi-log4j-utilizzando-spring

CẬP NHẬT (2009/08/01) Tôi đã dịch bài báo của mình sang tiếng Anh: http://www.megadix.it/node/136


6

Chỉ là một nhận xét về giải pháp của Iker .

ServletContextlà một giải pháp tốt cho vấn đề của bạn. Nhưng tôi không nghĩ rằng nó tốt cho việc duy trì. Hầu hết các tệp nhật ký thời gian được yêu cầu lưu trong thời gian dài.

ServletContexttạo tệp dưới tệp được triển khai, nó sẽ bị xóa khi máy chủ được triển khai lại. Đề xuất của tôi là đi với thư mục mẹ của rootPath thay vì thư mục con.


5

Không phải log4j chỉ sử dụng thư mục gốc của ứng dụng nếu bạn không chỉ định thư mục gốc trong thuộc tính đường dẫn FileAppender của mình? Vì vậy, bạn chỉ có thể sử dụng:

log4j.appender.file.File = logs / MyLog.log

Đã lâu kể từ khi tôi thực hiện phát triển web Java, nhưng đây có vẻ là cách trực quan nhất và cũng không va chạm với các nhật ký được đặt tên đáng tiếc khác ghi vào thư mục $ {catalina.home} / logs.


4
Từ những gì tôi đã thấy, nó có thể sử dụng thư mục chính của người dùng hoặc thư mục chính của vùng chứa nếu bạn không đưa ra một đường dẫn tuyệt đối. Không đáng tin cậy.
Iker Jimenez

1
@Iker: Tại sao bạn không thể đặt thư mục gốc của ứng dụng một cách rõ ràng trong vùng chứa hoặc cấu hình ứng dụng của mình? Khi bạn đã làm điều đó một lần trong quá trình phát triển và sản xuất, bạn có thể sử dụng các đường dẫn tương đối một cách đáng tin cậy. Giả sử thư mục gốc được đặt chính xác, các đường dẫn tương đối là giải pháp di động nhất (định vị lại).
Derek Mahar

1
Tôi biết đây là một bài viết cũ nhưng tôi muốn ghi chú về điều này cho những người khác. Đối với tomcat nếu thư mục gốc của ứng dụng không được đặt rõ ràng, tôi tin rằng nó được đặt mặc định cho bất kỳ thư mục nào bạn bắt đầu tomcat từ.
Geren White

2

Như một nhận xét khác về https://stackoverflow.com/a/218037/2279200 - điều này có thể bị hỏng, nếu ứng dụng web ngầm khởi động ServletContextListener khác, có thể được gọi sớm hơn và đã cố gắng sử dụng log4j - trong trường hợp này, Cấu hình log4j sẽ được đọc và phân tích cú pháp trước khi thuộc tính xác định thư mục gốc của log được thiết lập => các file log sẽ xuất hiện ở đâu đó bên dưới thư mục hiện tại (thư mục hiện tại khi khởi động tomcat).

Tôi chỉ có thể nghĩ đến giải pháp sau cho vấn đề này: - đổi tên tệp log4j.properties (hoặc logj4.xml) của bạn thành tệp mà log4j sẽ không tự động đọc. - Trong bộ lọc ngữ cảnh của bạn, sau khi đặt thuộc tính, hãy gọi lớp trợ giúp DOM / PropertyConfigurator để đảm bảo rằng log4j -. {Xml, properties} của bạn được đọc - Đặt lại cấu hình log4j (IIRC có một phương pháp để thực hiện điều đó)

Đây là một lực lượng hơi thô bạo, nhưng đó là cách duy nhất để làm cho nó kín nước.


1

Trong trường hợp bạn đang sử dụng Maven, tôi có một giải pháp tuyệt vời cho bạn:

  1. Chỉnh sửa tệp pom.xml của bạn để bao gồm các dòng sau:

    <profiles>
        <profile>
            <id>linux</id>
            <activation>
                <os>
                    <family>unix</family>
                </os>
            </activation>
            <properties>
                <logDirectory>/var/log/tomcat6</logDirectory>
            </properties>
        </profile>
        <profile>
            <id>windows</id>
            <activation>
                <os>
                    <family>windows</family>
                </os>
            </activation>
            <properties>
                <logDirectory>${catalina.home}/logs</logDirectory>
            </properties>
        </profile>
    </profiles>
    

    Ở đây bạn xác định thuộc logDirectorytính cụ thể cho họ hệ điều hành.

  2. Sử dụng thuộc tính đã được xác định logDirectorytrong log4j.propertiestệp:

    log4j.appender.FILE=org.apache.log4j.RollingFileAppender
    log4j.appender.FILE.File=${logDirectory}/mylog.log
    log4j.appender.FILE.MaxFileSize=30MB
    log4j.appender.FILE.MaxBackupIndex=10
    log4j.appender.FILE.layout=org.apache.log4j.PatternLayout
    log4j.appender.FILE.layout.ConversionPattern=%d{ISO8601} [%x] %-5p [%t] [%c{1}] %m%n
    
  3. Đó là nó!

Tái bút: Tôi chắc rằng điều này có thể đạt được bằng Ant nhưng tiếc là tôi không có đủ kinh nghiệm với nó.


1

Đề xuất của tôi là tệp nhật ký phải luôn được ghi bên trên ngữ cảnh gốc của webApp, vì vậy trong trường hợp chúng tôi triển khai lại webApp, chúng tôi không muốn ghi đè các tệp nhật ký hiện có.


1

Giải pháp của tôi tương tự như giải pháp của Iker Jimenez , nhưng thay vì sử dụng System.setProperty(...)tôi sử dụng org.apache.log4j.PropertyConfigurator.configure(Properties). Đối với điều đó, tôi cũng cần log4j để không thể tự tìm thấy cấu hình của nó và tôi tải nó theo cách thủ công (cả hai điểm được mô tả trong câu trả lời của Wolfgang Liebich ).

Điều này hoạt động đối với Jetty và Tomcat, độc lập hoặc chạy từ IDE, không yêu cầu cấu hình, cho phép đặt nhật ký của từng ứng dụng vào thư mục riêng của chúng, bất kể có bao nhiêu ứng dụng bên trong vùng chứa (đó là vấn đề với Systemgiải pháp dựa trên). Bằng cách này, người ta cũng có thể đặt tệp cấu hình log4j ở bất kỳ đâu bên trong ứng dụng web (ví dụ: trong một dự án, chúng tôi có tất cả tệp cấu hình bên trong WEB-INF/).

Chi tiết:

  1. Tôi có các thuộc tính của mình trong log4j-no-autoload.propertiestệp trong classpath (ví dụ: trong dự án Maven của tôi, nó ban đầu src/main/resources, được đóng gói vào WEB-INF/classes),
  2. Nó có tệp appender được định cấu hình như:

    log4j.appender.MyAppFileAppender = org.apache.log4j.FileAppender
    log4j.appender.MyAppFileAppender.file = ${webAppRoot}/WEB-INF/logs/my-app.log
    ...
    
  3. Và tôi có một trình nghe ngữ cảnh như thế này (ngắn hơn nhiều với cú pháp "try-with-resource" của Java 7):

    @WebListener
    public class ContextListener implements ServletContextListener {
        @Override
        public void contextInitialized(final ServletContextEvent event) {
            Properties props = new Properties();
            InputStream strm =
                ContextListener.class.getClassLoader()
                    .getResourceAsStream("log4j-no-autoload.properties");
            try {
                props.load(strm);
            } catch (IOException propsLoadIOE) {
                throw new Error("can't load logging config file", propsLoadIOE);
            } finally {
                try {
                    strm.close();
                } catch (IOException configCloseIOE) {
                    throw new Error("error closing logging config file", configCloseIOE);
                }
            }
            props.put("webAppRoot", event.getServletContext().getRealPath("/"));
            PropertyConfigurator.configure(props);
            // from now on, I can use LoggerFactory.getLogger(...)
        }
        ...
    }
    

1

Bạn có thể chỉ định đường dẫn tương đối đến tệp nhật ký, sử dụng thư mục công việc :

appender.file.fileName = ${sys:user.dir}/log/application.log

Điều này độc lập với vùng chứa servlet và không yêu cầu chuyển biến tùy chỉnh vào môi trường hệ thống.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.