Don't Use System.out.println! Use Log4j

本文介绍如何使用Log4j进行日志记录,包括配置文件详解、日志级别设置及不同输出格式的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


Home: www.vipan.comVipan Singlae-mail: vipan@vipan.com
Don't Use System.out.println!
Use Log4j

Quick Start to Using Log4j

  • Download the Log4j software (about 2.3MB as of log4j 1.1.3) and extract log4j.jar (about 156KB) out of it. Include log4j.jar in your application's classpath so that logging methods can find the needed classes. (I copied the log4j.jar file in java installation directory's lib/ext subdirectory because Java will automatically pick it up from there and add it to classpath!)
  • Save the following sample code in a file named TestLogging.java somewhere in the classpath.
    import org.apache.log4j.*;
    
    // How to use log4j
    public class TestLogging {
    
        // Initialize a logging category.  Here, we get THE ROOT CATEGORY
        //static Category cat = Category.getRoot();
        // Or, get a custom category
        static Category cat = Category.getInstance(TestLogging.class.getName());
    
        // From here on, log away!  Methods are: cat.debug(your_message_string),
        // cat.info(...), cat.warn(...), cat.error(...), cat.fatal(...)
    
        public static void main(String args[]) {
            // Try a few logging methods
            cat.debug("Start of main()");
            cat.info("Just testing a log message with priority set to INFO");
            cat.warn("Just testing a log message with priority set to WARN");
            cat.error("Just testing a log message with priority set to ERROR");
            cat.fatal("Just testing a log message with priority set to FATAL");
    
            // Alternate but INCONVENIENT form
            cat.log(Priority.DEBUG, "Calling init()");
    
            new TestLogging().init();
        }
    
        public void init() {
            java.util.Properties prop = System.getProperties();
            java.util.Enumeration enum = prop.propertyNames();
    
            cat.info("***System Environment As Seen By Java***");
            cat.debug("***Format: PROPERTY = VALUE***");
    
            while (enum.hasMoreElements()) {
                String key = (String) enum.nextElement();
                cat.info(key + " = " + System.getProperty(key));
            }
        }
    
    }

    Log4j by default can log messages with five priority levels.

    1. Use debug to write debugging messages which should not be printed when the application is in production.
    2. Use info for messages similar to the "verbose" mode of many applications.
    3. Use warn for warning messages which are logged to some log but the application is able to carry on without a problem.
    4. Use error for application error messages which are also logged to some log but, still, the application can hobble along. Such as when some administrator-supplied configuration parameter is incorrect and you fall back to using some hard-coded default value.
    5. Use fatal for critical messages, after logging of which the application quits abnormally.
  • Save the following lines in a file named log4j.properties in the same directory as TestLogging.class (after compiling TestLogging.java above). Log4j looks for this file by default in the application's classpath when you call getRoot() or getInstance("category_name") in your code.
    log4j.rootCategory=DEBUG, dest1
    log4j.appender.dest1=org.apache.log4j.ConsoleAppender
    log4j.appender.dest1.layout=org.apache.log4j.PatternLayout

    The PatternLayout defaults to %m%n which means print your-supplied message and a newline. This is exactly the same as printed out by Java's System.out.println(...) method, except that Log4j methods are shorter to write, among other great advantages!

  • Now, when you compile and run TestLogging.java, you will get output similar to:
    Start of main()
    Just testing a log message with priority set to INFO
    Just testing a log message with priority set to WARN
    Just testing a log message with priority set to ERROR
    Just testing a log message with priority set to FATAL
    Calling init()
    ***System Environment As Seen By Java***
    ***Format: PROPERTY = VALUE***
    java.runtime.name = Java(TM) 2 Runtime Environment, Standard Edition
    sun.boot.library.path = c:/jdk1.3/jre/bin
    java.vm.version = 1.3.0_02
    java.vm.vendor = Sun Microsystems Inc.
    ... and so on
  • If you want to print the priority (you assigned to the message) next to the message, append the following line to the end of the log4j.properties file and save it:
    log4j.appender.dest1.layout.ConversionPattern=%-5p: %m%n

    You will override the default %m%n. %p will print the message priority, %m is the message itself, %n is newline. Since you only made changes to a properties file, not to any Java code, there is no need to re-compile TestLogging.java. Just run it again. You should get the folowing output:

    DEBUG: Start of main()
    INFO : Just testing a log message with priority set to INFO
    WARN : Just testing a log message with priority set to WARN
    ERROR: Just testing a log message with priority set to ERROR
    FATAL: Just testing a log message with priority set to FATAL
    DEBUG: Calling init()
    INFO : ***System Environment As Seen By Java***
    DEBUG: ***Format: PROPERTY = VALUE***
    INFO : java.runtime.name = Java(TM) 2 Runtime Environment, Standard Edition
    INFO : sun.boot.library.path = c:/jdk1.3/jre/bin
    INFO : java.vm.version = 1.3.0_02
    INFO : java.vm.vendor = Sun Microsystems Inc.
    ... and so on
  • If you are tired of all those DEBUG and INFO messages and want to disable them but still want to log other messages (for example, when the application is ready to go into production), change the log4j.rootCategory=DEBUG, dest1 line to:
    log4j.rootCategory=WARN, dest1

    This line tells Log4j to skip messages with priority lower than WARN (such as DEBUG and INFO). Now, it will just print:

    WARN : Just testing a log message with priority set to WARN
    ERROR: Just testing a log message with priority set to ERROR
    FATAL: Just testing a log message with priority set to FATAL

Log4j Details

  • Log4j has three main components: categories, appenders and layouts.
    • You instantiate a category and then call its various logging methods to send your message strings to log(s).
    • A category is configured to log to one or more destinations or targets. These logging destinations are called "appenders" in Log4j, probably because these classes by default "append" your message string to the end of the log. Log4j can send your log messages to the console, a text file, an html file, an xml file, a socket or even to the Windows NT Event Log, all with one logging call. It can even send your log message as an email (desirable for fatal errors, for example).
    • Some appender classes are ConsoleAppender, FileAppender, SMTPAppender, SocketAppender, NTEventLogAppender, SyslogAppender, JMSAppender, AsyncAppender and NullAppender.
    • An appender uses a layout to format your message before actually writing it to the log. For example, the HTMLLayout will format all your messages into a nice HTML table.
    • In addition to logging the message that you send, Log4j can also log the date, time, message priority (DEBUG, WARN, FATAL etc.), Java class name, source code line number, method name, Java thread name and much more. What to log is specified in the layout which an appender s configured with.
<script src="http://pagead2.googlesyndication.com/pagead/show_ads.js" type="text/javascript"> </script>
  • The name of a Category is a case-sensitive string of dot-separated words which you supply. For convenience, you will usually use your-class-name.class.getName() to get the fully-qualified name of the Java class and set it as the category name. (In addition, at run time, logging the category name is much faster than logging the class name. If you want speed and class name, use the class name as the name of the category and then, just log the category name!)
  • Each word in the category name is said to be an ancestor of the subsequent words and a parent of the immediately following word. This is important because Log4j has this concept of inheriting priorities and appenders from ancestors until overridden in a particular category.
  • There is always a root category which has no name, just like the root of an XML document. Use static methods in the Category class to instantiate categories:
    Category cat = Category.getRoot();
    Category cat2 = Category.getInstance("your.category.name");

    For a particular Category name, Category.getInstance(...) method always returns the exact same Category object. So, you don't need to instantiate a Category and keep passing it around. Instead, anywhere you need it, just say Category.getInstance("wanted.category.name");.

  • Typical category instantiation for a class named "TestApp" in a package named "com.comp":
    static Category cat = Category.getInstance(TestApp.class.getName());

    Note that TestApp is the class name itself, not an instance. Also, the TestApp.class statement does not create a new instance of class TestApp.

  • Five priority constants in the Priority class are FATAL, ERROR, WARN, INFO and DEBUG, in the order of decreasing priority. You can make more by subclassing if you want.
  • You can assign priorities to categories, but you don't have to. If you don't assign a priority to a category, log4j will walk up its ancestor hierarchy and use the first one it finds as assigned. The root category always has a priority assigned to it (default is Priority.DEBUG), so all categories are always guaranteed to have a priority.
  • Typical logging methods in the Category class:
    // Generic logging method (INCONVENIENT)
    public void log(Priority p, Object message);
    
    // Convenient shortcuts to the generic logging method
    public void debug(Object message);
    public void info(Object message);
    public void warn(Object message);
    public void error(Object message);
    public void fatal(Object message);
  • log4j only logs if and only if the category's log(...) method specifies a priority which is equal to or higher than the category's assigned or inherited priority.
    Category cat = Category.getRoot();
    cat.setPriority(Priority.ERROR);
    // Later...
    //cat.info("Started processing..."); //Will not log
    cat.error("User input is erroneous!"); //Will log
    cat.fatal("Cannot process user input.  Program terminated!"); //Will log

Configuring Log4j

  • Before logging is enabled, you have to configure log4j first. Configuring Log4j means adding appenders (destinations/targets/handlers) to categories (loggers) and assigning a layout (formatter) to each appender.
  • Categories can be created and configured in any order. In particular, a category will find and link to its descendants even if it is instantiated after them.
  • You will usually configure Log4j just once which is during application initialization, usually by reading a configuration file.
  • You can also configure a category programmatically to log to a particular appender using a category object's addAppender() method. Add as many as you want (or none if you don't). This method is usually not recommended as it involves changing the source code and recompiling it. The better way is to use external configuration files such as a Java properties file or an XML file.
  • Beware that appenders are not singletons, they are additive! A category inherits all the appenders from its ancestors also (by default). If you add an appender to a category and it writes to the same underlying stream (console, same file etc.) as some other appender, the same log message will appear twice (or more) in the log. In addition, if two categories in a hierarchy are configured to use the same appender name, Log4j will write twice to that appender. Use cat.setAdditivity(false) on a category to disable inheriting of appenders. Then, log messages will only be sent to the appenders specifically configured for that category.
  • Calling the static BasicConfigurator.configure() method configures log4j to log to console just like a system.out.println(...) statement would. This method is hardwired to add to the root category a ConsoleAppender printing on the console. The output will be formatted using a PatternLayout set to the pattern %-4r [%t] %-5p %c %x - %m%n. This is usually not needed as using log4j.properties file is simpler and more flexible.
  • The default file which configures log4j is log4j.properties. Place it anywhere in the application classpath.
  • Use one of the static PropertyConfigurator.configure(...) methods to read in the configuration information from a Java properties file. The existing configuration is neither cleared nor reset. To do so, call the static BasicConfigurator.resetConfiguration() method before calling this method. This has the potential to make long-term code management more complicated. For starters, stick with one log4j.properties in each application's root directory.
  • A sample log4j configuration file:
    # Set root category priority to DEBUG and set its only appender to A1
    log4j.rootCategory=DEBUG, A1
    
    # A1 is set to be a ConsoleAppender (writes to system console).
    log4j.appender.A1=org.apache.log4j.ConsoleAppender
    
    # A1 uses PatternLayout.
    log4j.appender.A1.layout=org.apache.log4j.PatternLayout
    log4j.appender.A1.layout.ConversionPattern=%-4r [%t] %-5p %c %x - %m%n

    This sample file configures log4j in exactly the same way as the BasicConfigurator.configure() method. But this way, you can later change, for example, DEBUG to FATAL in the properties file and not have to recompile any Java code!

  • Another more complex log4j configuration file:
    #### Use two appenders, one to log to console, another to log to a file
    log4j.rootCategory=debug, stdout, R
    
    # Print only messages of priority WARN or higher for your category
    log4j.category.your.category.name=WARN
    # Specifically inherit the priority level
    #log4j.category.your.category.name=INHERITED
    
    #### First appender writes to console
    log4j.appender.stdout=org.apache.log4j.ConsoleAppender
    log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
    
    # Pattern to output the caller's file name and line number.
    log4j.appender.stdout.layout.ConversionPattern=%5p [%t] (%F:%L) - %m%n
    
    #### Second appender writes to a file
    log4j.appender.R=org.apache.log4j.RollingFileAppender
    log4j.appender.R.File=example.log
    
    # Control the maximum log file size
    log4j.appender.R.MaxFileSize=100KB
    # Archive log files (one backup file here)
    log4j.appender.R.MaxBackupIndex=1
    
    log4j.appender.R.layout=org.apache.log4j.PatternLayout
    log4j.appender.R.layout.ConversionPattern=%p %t %c - %m%n

    Here, output will also be appended to the "example.log" file. This file will be rolled over when it reaches 100KB. When roll-over occurs, the old version of example.log is automatically moved to example.log.1. Also available is DailyRollingFileAppender to roll a log file every minute, hour, day, week, month or twice-a-day.

  • For long-lived programs such as servers, use configureAndWatch(String configFilename, long delay_in_milliseconds) to configure using a Java properties file. Log4j will keep monitoring the property file for any changes every so many milliseconds, and if the file changes, Log4j will update the configuration.
  • When moving code to production, for speedy performance, you can disable logging wholesale equal to or below a certain priority for all categories in a hierarchy. For example, To log nothing at all ever, specify log.disable=FATAL in the configuration properties file.

    Only exception is if log.disableOverride property is not set to false. So, a system administrator can (temporarily) specify log.disableOverride=true in the configuration file to override log.disable property and log all messages to troubleshoot something.

Useful Layouts

  • Some layout classes are TTCCLayout, HTMLLayout, PatternLayout, SimpleLayout and XMLLayout.
  • SimpleLayout and PatternLayout classes ignore Java Throwable errors and exceptions. HTMLLayout and XMLLayout handle them.
  • SimpleLayout consists of the priority of the log statement, followed by " - " and then the log message itself. For example:
    DEBUG - Hello world
  • PatternLayout lets you specify the output format according to conversion patterns similar to the C language printf function. For example, PatternLayout with the conversion pattern %r [%t] %-5p %c - %m%n will output something like:
    176 [main] INFO  org.foo.Bar - Located nearest gas station.
    • The first field is the number of milliseconds elapsed since the start of the program.
    • The second field is the thread making the log request.
    • The third field is the priority of the log statement.
    • The fourth field is the name of the category associated with the log request.
    • The text after the '-' is the message of the statement.
  • You can insert any literal text within the conversion pattern.
  • Conversion characters are:
      • %m: Outputs your message.
      • %p: Outputs the priority of the logging event.
      • %r: Outputs the number of milliseconds elapsed since the start of the application until the creation of the logging event.
      • %c: Outputs the category of the logging event. Example: For the category name "a.b.c", the pattern %c{2} will output "b.c". {2} means "output last two components of the dot-separated category name". If no {n} is there, full Category name is output by default.
      • %t: Outputs the name of the thread that generated the logging event.
      • %x: Outputs the nested diagnostic context (NDC) associated with the thread that generated the logging event. Useful when multiple clients are handled by separate threads such as in Java servlets. : Outputs the platform-dependent newline character(s). Preferable to specifying "/n" or "/r/n" etc. The date conversion specifier may be followed by a date format specifier enclosed between braces. Example: %d{HH:mm:ss,SSS} or %d{dd MMM yyyy HH:mm:ss,SSS}. If no date format specifier is given then ISO8601 format is assumed. The date format specifier admits the same syntax as the time pattern string of Java's SimpleDateFormat which is slow. For faster performance, use %d{ISO8601} (millisecs since program start, fastest) or %d{DATE} which use log4j's ISO8601DateFormat, AbsoluteTimeDateFormat, RelativeTimeDateFormat and DateTimeDateFormat date formatters respectively. : Outputs source code location information. Shortcut for %C.%M(%F:%L). , %d{ABSOLUTE}, %d{RELATIVE}
      • %n
      • %%: Outputs a single percent sign.
      • WARNING: The patterns below will slow down the execution of your program somewhat. Avoid unless execution speed is not an issue.
      • %d: Outputs the date of the logging event.
      • %l
      • %C: Outputs the fully-qualified class name of the caller issuing the logging request. Example: For the class name "org.apache.xyz.SomeClass", the pattern %C{1} will output "SomeClass". {1} means "output last one component of the fully-qualified class name". If no {n} is there, full class name is output by default.
      • %M: Outputs the method name where the logging request was issued.
      • %F: Outputs the file name where the logging request was issued.
      • %L: Outputs the line number from where the logging request was issued.
  • Optionally, use format modifiers between the percent sign and the conversion character to change the minimum field width, the maximum field width and text justification within a field.
    • Use the minus sign (-) to left-justify within a field. Default is to right-justify (pad on left).
    • Use a positive integer to specify the minimum field width. If the data item requires fewer characters, it is padded with space(s) on either the left or the right until the minimum width is reached. If the data item is larger than the minimum field width, the field is expanded to accommodate the data.
    • Use a period followed by a positive integer to specify the maximum field width. If the data item is longer than the maximum field, then the extra characters are removed from the beginning of the data item and not from the end. For example, it the maximum field width is eight and the data item is ten characters long, then the first two characters of the data item are dropped. This behavior deviates from the printf function in C where truncation is done from the end.
  • Examples:
      • %20c: Right-justify (by default) the category name within 20 spaces minimum.
      • %-20c: Left-justify the category name within 20 spaces minimum.
      • %.30c: If the category name is longer than 30 characters, truncate (from the beginning). No minimum width and therefore, no padding if shorter than 30 characters.
      • %20.30c: Right-justify if the category name is shorter than 20 characters. If category name is longer than 30 characters, then truncate from the beginning.
      • %-20.30c: Left-justify if the category name is shorter than 20 characters. If category name is longer than 30 characters, then truncate from the beginning.
      • %r [%t] %-5p %c %x - %m/n: This is essentially the TTCCLayout.
      • %-6r [%15.15t] %-5p %30.30c %x - %m/n: Similar to the TTCCLayout except that the relative time is right padded if less than 6 digits, thread name is right padded if less than 15 characters and truncated if longer, and the category name is left padded if shorter than 30 characters and truncated if longer.
  • ADVANCED: You don't have to pass just Strings to log. You can pass your objects to the log method also. Implement ObjectRenderer to log a string representation of your object to the appender.

Property Keys for Log4j, Appender and Layout Options

  • Overall Options for log4j

  • log4j.configuration=app_config.properties: First call to Category.getRoot() or Category.getInstance(...) method makes Log4j go through an initialization process. (You can watch that happening by setting "log4j.debug=true".) During this process, Log4j looks in the application's classpath for a "log4j.properties" file or the properties file you specify via the this property key. However, you need to set this as a system property, for example by running your program with java -Dlog4j.configuration=app_config.properties .... This is because, if you set it in the configuration file, it is too late. Log4j would have already started to read the log4j.properties file by default, if available!
  • log4j.debug=true: Default is false. Outputs verbose info as log4j is configuring itself.
  • log4j.disable=INFO: For all categories, do not log messages with priority equal to or lower than the one specified here. Works only if log4j.disableOverride property is false which is the default.
  • log4j.additivity.your.category.name=false: Default is true. Specifies whether the appenders should be accumulated from ancestors (true) or not (false).
  • log4j.defaultInitOverride=false: First call to Category.getRoot() or Category.getInstance(...) method makes Log4j go through an initialization process. (You can watch that happening by setting "log4j.debug=true".) During this process, Log4j looks in the application's classpath for a "log4j.properties" file or the properties file you specify via the "log4j.configuration=app_config.properties" property. If you don't want this to happen, set this property to true. However, you need to set this as a system property, for example by running your program with java -Dlog4j.defaultInitOverride=true .... This is because, if you set it in the configuration file, it is too late. Log4j would have already started to read the file!

    ConsoleAppender Options

  • log4j.disableOverride=false: Default is false. Sometimes set to true to be able to ignore log.disable property.
  • Threshold=WARN: This appender will not log any messages with priority lower than the one specified here even if the category's priority is set lower. This is useful to cut down the number of messages, for example, in a file log while displaying all messages on the console.
  • ImmediateFlush=true: Default is true, meaning log messages are not buffered at all which is what you want almost all of the time.
  • Target=System.err: Default is System.out.

    FileAppender Options

  • Threshold=WARN: Appender will not log any messages with priority lower than the one specified here even if the category's priority is set lower. This is useful to cut down the number of messages, for example, in a file log while displaying all messages on the console.
  • ImmediateFlush=true: Default is true, meaning log messages are not buffered at all which is what you want almost all the time.
  • File=mylog.txt: File name to log to. You can use ${some_property_key} (for example, java.home or user.home system properties) to specify the path in front. In fact, all options property keys accept this kind of value.
  • Append=false: Default is true which means append to the end of the file. false overwrites the file at the start of each program run.

    RollingFileAppender Options

  • Threshold=INFO: Appender will not log any messages with priority lower than the one specified here even if the category's priority is set lower. This is useful to cut down the number of messages, for example, in a file log while displaying all messages on the console.
  • ImmediateFlush=true: Default is true, meaning log messages are not buffered at all which is what you want almost all the time.
  • File=mylog.txt: File name to log to. You can use ${some_property_key} (for example, java.home or user.home system properties) to specify the path in front. In fact, all options property keys accept this kind of value.
  • Append=false: Default is true which means append to the end of the file. false overwrites the file at the start of each program run.
  • MaxFileSize=100KB: Suffixes can be KB, MB or GB. Roll the log file after this size is reached.
  • MaxBackupIndex=2: Maintain a maximum of 2 (for example) backup files. Erases oldest file(s). 0 means no backup files at all.

    DailyRollingFileAppender Options

  • Threshold=WARN: Appender will not log any messages with priority lower than the one specified here even if the category's priority is set lower. This is useful to cut down the number of messages, for example, in a file log while displaying all messages on the console.
  • ImmediateFlush=true: Default is true, meaning log messages are not buffered at all which is what you want almost all the time.
  • File=mylog.txt: File name to log to. You can use ${some_property_key} (for example, java.home or user.home system properties) to specify the path in front. In fact, all options property keys accept this kind of value.
  • Append=false: Default is true which means append to the end of the file. false overwrites the file at the start of each program run.

    PatternLayout Options

    HTMLLayout Options

  • DatePattern='.'yyyy-ww: Roll the file every week. You can specify rolling frequency to be monthly, weekly, daily, twice-a-day, hourly and even every minute. The value not only specifies the rolling frequency but also specifies the suffix for the backup file. Do not use the colon (:) character anywhere in the value. Other than that, you can use any format string that Java's SimpleDateFormat will accept. In particular, you must escape literal text within a pair of single quotes (like the '.' in the example here.)
    • '.'yyyy-MM: Roll log file on the first of each month
    • '.'yyyy-ww: Roll log file at the start of each week
    • '.'yyyy-MM-dd: Roll log file at midnight everyday
    • '.'yyyy-MM-dd-a: Roll log file at midnight and midday everyday
    • '.'yyyy-MM-dd-HH: Roll log file at the start of every hour
    • '.'yyyy-MM-dd-HH-mm: Roll log file at the beginning of every minute
  • ConversionPattern=%m%n: How to format each log message (What information to include).
  • LocationInfo=true: Default is false. Outputs Java file name and line number.
  • Title=My app title: Default is Log4J Log Messages. Sets the <title> tag of HTML.

    XMLLayout Options

  • LocationInfo=true: Default is false. Outputs Java file name and line number.

    TTCCLayout Options (Use PatternLayout instead which is more flexible)

  • DateFormat=ISO8601: Either an argument to the constructor of Java's SimpleDateFormat or one of the strings NULL, RELATIVE, ABSOLUTE, DATE or ISO8601.
  • TimeZoneID=GMT-8:00: String in the format expected by the TimeZone.getTimeZone(java.lang.String) method.
  • CategoryPrefixing=false: Default is true. Outputs category name.
  • ContextPrinting=false: Default is true. Outputs nested diagnostic context information belonging to the current thread.
  • ThreadPrinting=false: Default is true. Outputs thread name.

A Comprehensive Log4J Configuration Properties File

#log4j.debug=true
#log4j.disable=fatal
#log4j.additivity.TestLogging=false

log4j.rootCategory=, dest1
log4j.category.TestLogging=DEBUG, dest1
log4j.appender.dest1=org.apache.log4j.ConsoleAppender
#log4j.appender.dest1.layout=org.apache.log4j.SimpleLayout
log4j.appender.dest1.layout=org.apache.log4j.PatternLayout
#log4j.appender.dest1.layout.ConversionPattern=%-5p %l %x: %m%n


!----------------------####### END OF PROPERTIES #######----------------------!


###############################################################################
# Below I document in more detail how to write a log4j configuration file.    #
# SELECTIVELY copy lines beginning with #, paste and uncomment them above.    #
###############################################################################

!-----------------------------------------------------------------------------!
! PLACE THIS FILE ANYWHERE IN CLASSPATH                                       !
! Appenders are additive by default.                                          !
! Priorities are inherited until overridden in a category.                    !
! In ${property_key}, the value of the key can be defined as a system         !
! property or in this file itself.  System properties are searched first and  !
! then this file.                                                             !
!-----------------------------------------------------------------------------!



!-----------------------------------------------------------------------------!
! Configure log4j's operation at the meta level                               !
!-----------------------------------------------------------------------------!
! Observe log4j parsing this file
#log4j.debug=true
! Set this to false for log4j to actually obey the log4j.disable property(next)
#log4j.disableOverride=false
! Disable all logging in all categories for messages with priority equal to
! or lower than the one given here
#log4j.disable=INFO



!-----------------------------------------------------------------------------!
! Configure categories (loggers)                                              !
!-----------------------------------------------------------------------------!

! ROOT CATEGORY (Usually sufficient to set this one only)
! Here, logs messages with priority DEBUG (default) or higher
#log4j.rootCategory=, dest1
! Or,
#log4j.rootCategory=debug, dest1, dest2

! YOUR CATEGORIES (to customize logging per class/pkg/project/etc)
! Here, overrides ancestor's priority and makes it WARN or higher for this cat.
#log4j.category.TestLogging=WARN, dest3
! Or,
#log4j.category.TestLogging=DEBUG, dest3

!--------DON'T DO THIS!!!  APPENDERS ARE ADDITIVE BY DEFAULT!!!---------------!
! It will write the same log message TWICE to dest1. Once for root, then for  !
! this category.                                                              !
!#log4j.category.TestLogging=DEBUG, dest1, dest3                              !
! If you DO NOT want additivity for this category, say so                     !
!#log4j.additivity.TestLogging=false                                          !
!-----------------------------------------------------------------------------!



!-----------------------------------------------------------------------------!
! Configure appenders (log destinations/targets) and their options            !
!-----------------------------------------------------------------------------!

! WRITE TO CONSOLE (stdout or stderr)
#log4j.appender.dest1=org.apache.log4j.ConsoleAppender
#log4j.appender.dest1.ImmediateFlush=true

! WRITE LOG TO A FILE, ROLL THE FILE AFTER SOME SIZE
#log4j.appender.dest2=org.apache.log4j.RollingFileAppender
! This appender will only log messages with priority equal to or higher than
! the one specified here
#log4j.appender.dest2.Threshold=ERROR
! Specify the file name (${property_key} gets substituted with its value)
#log4j.appender.dest2.File=${java.home}/log4j.log
! Don't append, overwrite
#log4j.appender.dest2.Append=false
! Control the maximum log file size
#log4j.appender.dest2.MaxFileSize=100KB
! Keep backup file(s) (backups will be in filename.1, .2 etc.)
#log4j.appender.dest2.MaxBackupIndex=2

! WRITE LOG TO A FILE, ROLL THE FILE EVERY WEEK
#log4j.appender.dest3=org.apache.log4j.DailyRollingFileAppender
! Specify the file name
#log4j.appender.dest3.File=log4TestLogging2.html
! Control the maximum log file size
#log4j.appender.dest3.MaxFileSize=300KB
! Rollover log file at the start of each week
#log4j.appender.dest3.DatePattern='.'yyyy-ww



!-----------------------------------------------------------------------------!
! Configure appender layouts (log formats) and their options                  !
!-----------------------------------------------------------------------------!

! USE SIMPLE LOG FORMAT (e.g. INFO - your log message)
#log4j.appender.dest1.layout=org.apache.log4j.SimpleLayout

! USE A C PRINTF STYLE PATTERN TO FORMAT LOG MESSAGE
#log4j.appender.dest1.layout=org.apache.log4j.PatternLayout
! For a pattern layout, specify the pattern (Default is %m%n which is fastest)
#log4j.appender.dest1.layout.ConversionPattern=%-5p: %m%n
! Or,
#log4j.appender.dest1.layout.ConversionPattern=%-5p %6.10r[%t]%x(%F:%L) - %m%n

#log4j.appender.dest2.layout=org.apache.log4j.PatternLayout
#log4j.appender.dest2.layout.ConversionPattern=[%d{ISO8601}]%5p%6.6r[%t]%x(%F:%L) - %m%n
! Or, (the pattern below will slow down your app)
#log4j.appender.dest2.layout.ConversionPattern=[%d{yyyy-mm-dd hh:mm},%6.6r]%-5p[%t]%x(%F:%L) - %m%n


! FORMAT LOG MESSAGES IN THE FORM OF AN HTML TABLE
#log4j.appender.dest3.layout=org.apache.log4j.HTMLLayout
! Include Java file name and line number (Default is false)
#log4j.appender.dest3.layout.LocationInfo=true
! Set <title> tag (Default: Log4J Log Messages)
#log4j.appender.dest3.layout.Title=My App Log


!-----------------------------------------------------------------------------!
!                          PATTERN FORMATS GLOSSARY                           !
!-----------------------------------------------------------------------------!
! %n - newline                                                                !
! %m - your log message                                                       !
! %p - message priority (FATAL, ERROR, WARN, INFO, DEBUG or custom)           !
! %r - millisecs since program started running                                !
! %% - percent sign in output                                                 !
!                                                                             !
!-----------------------SOME MORE CLUTTER IN YOUR LOG-------------------------!
! %c - name of your category (logger), %c{2} will outputs last two components !
! %t - name of current thread                                                 !
! %x - Nested Diagnostic Context (NDC) (you supply it!)                       !
!                                                                             !
!-------------------------SLOW PERFORMANCE FORMATS----------------------------!
! %d - date and time, also %d{ISO8601}, %d{DATE}, %d{ABSOLUTE},               !
!        %d{HH:mm:ss,SSS}, %d{dd MMM yyyy HH:mm:ss,SSS} and so on             !
! %l - Shortcut for %F%L%C%M                                                  !
! %F - Java source file name                                                  !
! %L - Java source line number                                                !
! %C - Java class name, %C{1} will output the last one component              !
! %M - Java method name                                                       !
!                                                                             !
!------------------------------FORMAT MODIFIERS-------------------------------!
! %-any_letter_above - Left-justify in min. width (default is right-justify)  !
! %20any_letter_above - 20 char. min. width (pad with spaces if reqd.)        !
! %.30any_letter_above - 30 char. max. width (truncate beginning if reqd.)    !
! %-10.10r - Example.  Left-justify time elapsed within 10-wide field.        !
!              Truncate from beginning if wider than 10 characters.           !
!-----------------------------------------------------------------------------!


!-----------------------------------------------------------------------------!
!                             OPTIONS GLOSSARY                                !
!-----------------------------------------------------------------------------!
!-------------------------OVERALL OPTIONS FOR log4j---------------------------!
! Specify as command line option: -Dlog4j.defaultInitOverride=false
! Specify as command line option: -Dlog4j.configuration=app_config.properties
!#log4j.debug=true
!#log4j.disable=INFO
!#log4j.disableOverride=false
!#log4j.additivity.your.category.name=false
!
!----------------------------NullAppender OPTIONS-----------------------------!
!#log4j.appender.dest1.Threshold=INFO
!
!---------------------------ConsoleAppender OPTIONS---------------------------!
!#log4j.appender.dest1.Threshold=INFO
!#log4j.appender.dest1.ImmediateFlush=true
!#log4j.appender.dest1.Target=System.err
!
!-----------------------------FileAppender OPTIONS----------------------------!
!#log4j.appender.dest2.Threshold=INFO
!#log4j.appender.dest2.ImmediateFlush=true
!#log4j.appender.dest2.File=mylog.txt
!#log4j.appender.dest2.Append=false
!
!-------------------------RollingFileAppender OPTIONS-------------------------!
!#log4j.appender.dest2.Threshold=INFO
!#log4j.appender.dest2.ImmediateFlush=true
!#log4j.appender.dest2.File=mylog.txt
!#log4j.appender.dest2.Append=false
!#log4j.appender.dest2.MaxFileSize=100KB
!#log4j.appender.dest2.MaxBackupIndex=2
!
!-----------------------DailyRollingFileAppender OPTIONS----------------------!
!#log4j.appender.dest2.Threshold=INFO
!#log4j.appender.dest2.ImmediateFlush=true
!#log4j.appender.dest2.File=mylog.txt
!#log4j.appender.dest2.Append=false
!#log4j.appender.dest2.DatePattern='.'yyyy-ww
!
!-----------------------------SimpleLayout OPTIONS----------------------------!
!**None**
!
!-------------TTCCLayout OPTIONS (PatternLayout is more flexible)-------------!
!#log4j.appender.dest1.layout.DateFormat=ISO8601
!#log4j.appender.dest1.layout.TimeZoneID=GMT-8:00
!#log4j.appender.dest1.layout.CategoryPrefixing=false
!#log4j.appender.dest1.layout.ThreadPrinting=false
!#log4j.appender.dest1.layout.ContextPrinting=false
!
!-----------------------------PatternLayout OPTIONS---------------------------!
!#log4j.appender.dest1.layout.ConversionPattern=%m%n
!
!-------------------------------HTMLLayout OPTIONS----------------------------!
!#log4j.appender.dest3.layout.LocationInfo=true
!#log4j.appender.dest3.layout.Title=My app title
!
!--------------------------------XMLLayout OPTIONS----------------------------!
!#log4j.appender.dest3.layout.LocationInfo=true
!-----------------------------------------------------------------------------!

Miscellaneous Information

  • Where performance is "way too crucial" in your code, to save the time to construct the parameters of a logging method, use:
    if (cat.isDebugEnabled() {
        cat.debug("Entry number: " + i + " is " + String.valueOf(entry[i]));
    }
  • When configured with the SimpleLayout or a pattern using only %p, %m, %n, performance tests have shown log4j to log as quickly as System.out.println(...).
  • Using Nested Diagnostics Contexts: Sometimes, for example in Servlets, one code serves many clients by instantiating a different thread to serve each client. You can use nested diagnostics contexts (NDC) to differentiate logging requests from different clients. All you do is push a String unique to the client on to NDC before logging. The unique string can be the client's IP address, hostname or anything you desire to associate with that request. Specify %x in your layout pattern to print that string in that request's log messages.
    import org.apache.log4j.*;
    
    public class TestNDC {
    
      // !Make sure log4j.properties file exists in classpath
    
      static Category cat = Category.getInstance(TestNDC.class.getName());
    
      public static void main(String[] args) {
    
        // Nested Diagnostic Configurator to identify the client for
        // multithreaded client request processors such as servlets, if needed
        NDC.push("Client #45890");
    
        cat.info("Testing Nested Diagnostic Context (NDC).");
        cat.info("Make sure %x is in layout pattern!");
        cat.info("Client #45890 should appear in this log message.");
    
        switchNDC();
    
        cat.info("Finished.");
      }
    
      static void switchNDC() {
        NDC.push("Client #99999");
        cat.debug("Client #99999 should appear nested in this log message.");
        NDC.pop();
      }
    
    }

© Vipan Singla 2001
/*! ****************************************************************************** * * Pentaho Data Integration * * Copyright (C) 2002-2019 by Hitachi Vantara : http://www.pentaho.com * ******************************************************************************* * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ******************************************************************************/ package org.pentaho.di.core.logging; import java.util.Date; import java.util.Queue; import java.util.Map; import org.pentaho.di.core.Const; import org.pentaho.di.core.util.Utils; import org.pentaho.di.core.metrics.MetricsSnapshot; import org.pentaho.di.core.metrics.MetricsSnapshotInterface; import org.pentaho.di.core.metrics.MetricsSnapshotType; public class LogChannel implements LogChannelInterface { public static LogChannelInterface GENERAL = new LogChannel( "General" ); public static LogChannelInterface METADATA = new LogChannel( "Metadata" ); public static LogChannelInterface UI = new LogChannel( "GUI" ); private String logChannelId; private LogLevel logLevel; private String containerObjectId; private boolean gatheringMetrics; private boolean forcingSeparateLogging; private static MetricsRegistry metricsRegistry = MetricsRegistry.getInstance(); private String filter; private LogChannelFileWriterBuffer fileWriter; public LogChannel( Object subject ) { logLevel = DefaultLogLevel.getLogLevel(); logChannelId = LoggingRegistry.getInstance().registerLoggingSource( subject ); } public LogChannel( Object subject, boolean gatheringMetrics ) { this( subject ); this.gatheringMetrics = gatheringMetrics; } public LogChannel( Object subject, LoggingObjectInterface parentObject ) { if ( parentObject != null ) { this.logLevel = parentObject.getLogLevel(); this.containerObjectId = parentObject.getContainerObjectId(); } else { this.logLevel = DefaultLogLevel.getLogLevel(); this.containerObjectId = null; } logChannelId = LoggingRegistry.getInstance().registerLoggingSource( subject ); } public LogChannel( Object subject, LoggingObjectInterface parentObject, boolean gatheringMetrics ) { this( subject, parentObject ); this.gatheringMetrics = gatheringMetrics; } @Override public String toString() { return logChannelId; } @Override public String getLogChannelId() { return logChannelId; } /** * * @param logMessage * @param channelLogLevel */ public void println( LogMessageInterface logMessage, LogLevel channelLogLevel ) { String subject = null; LogLevel logLevel = logMessage.getLevel(); if ( !logLevel.isVisible( channelLogLevel ) ) { return; // not for our eyes. } if ( subject == null ) { subject = "Kettle"; } // Are the message filtered? // if ( !logLevel.isError() && !Utils.isEmpty( filter ) ) { if ( subject.indexOf( filter ) < 0 && logMessage.toString().indexOf( filter ) < 0 ) { return; // "filter" not found in row: don't show! } } // Let's not keep everything... // if ( channelLogLevel.getLevel() >= logLevel.getLevel() ) { KettleLoggingEvent loggingEvent = new KettleLoggingEvent( logMessage, System.currentTimeMillis(), logLevel ); KettleLogStore.getAppender().addLogggingEvent( loggingEvent ); if ( this.fileWriter == null ) { this.fileWriter = LoggingRegistry.getInstance().getLogChannelFileWriterBuffer( logChannelId ); } // add to buffer if ( this.fileWriter != null ) { this.fileWriter.addEvent( loggingEvent ); } } } public void println( LogMessageInterface message, Throwable e, LogLevel channelLogLevel ) { println( message, channelLogLevel ); String stackTrace = Const.getStackTracker( e ); LogMessage traceMessage = new LogMessage( stackTrace, message.getLogChannelId(), LogLevel.ERROR ); println( traceMessage, channelLogLevel ); } @Override public void logMinimal( String s ) { println( new LogMessage( s, logChannelId, LogLevel.MINIMAL ), logLevel ); } @Override public void logBasic( String s ) { println( new LogMessage( s, logChannelId, LogLevel.BASIC ), logLevel ); } @Override public void logError( String s ) { println( new LogMessage( s, logChannelId, LogLevel.ERROR ), logLevel ); } @Override public void logError( String s, Throwable e ) { println( new LogMessage( s, logChannelId, LogLevel.ERROR ), e, logLevel ); } @Override public void logBasic( String s, Object... arguments ) { println( new LogMessage( s, logChannelId, arguments, LogLevel.BASIC ), logLevel ); } @Override public void logDetailed( String s, Object... arguments ) { println( new LogMessage( s, logChannelId, arguments, LogLevel.DETAILED ), logLevel ); } @Override public void logError( String s, Object... arguments ) { println( new LogMessage( s, logChannelId, arguments, LogLevel.ERROR ), logLevel ); } @Override public void logDetailed( String s ) { println( new LogMessage( s, logChannelId, LogLevel.DETAILED ), logLevel ); } @Override public void logDebug( String s ) { println( new LogMessage( s, logChannelId, LogLevel.DEBUG ), logLevel ); } @Override public void logDebug( String message, Object... arguments ) { println( new LogMessage( message, logChannelId, arguments, LogLevel.DEBUG ), logLevel ); } @Override public void logRowlevel( String s ) { println( new LogMessage( s, logChannelId, LogLevel.ROWLEVEL ), logLevel ); } @Override public void logMinimal( String message, Object... arguments ) { println( new LogMessage( message, logChannelId, arguments, LogLevel.MINIMAL ), logLevel ); } @Override public void logRowlevel( String message, Object... arguments ) { println( new LogMessage( message, logChannelId, arguments, LogLevel.ROWLEVEL ), logLevel ); } @Override public boolean isBasic() { return logLevel.isBasic(); } @Override public boolean isDebug() { return logLevel.isDebug(); } @Override public boolean isDetailed() { try { return logLevel.isDetailed(); } catch ( NullPointerException ex ) { // System.out.println( "Oops!" ); return false; } } @Override public boolean isRowLevel() { return logLevel.isRowlevel(); } @Override public boolean isError() { return logLevel.isError(); } @Override public LogLevel getLogLevel() { return logLevel; } @Override public void setLogLevel( LogLevel logLevel ) { this.logLevel = logLevel; } /** * @return the containerObjectId */ @Override public String getContainerObjectId() { return containerObjectId; } /** * @param containerObjectId * the containerObjectId to set */ @Override public void setContainerObjectId( String containerObjectId ) { this.containerObjectId = containerObjectId; } /** * @return the gatheringMetrics */ @Override public boolean isGatheringMetrics() { return gatheringMetrics; } /** * @param gatheringMetrics * the gatheringMetrics to set */ @Override public void setGatheringMetrics( boolean gatheringMetrics ) { this.gatheringMetrics = gatheringMetrics; } @Override public boolean isForcingSeparateLogging() { return forcingSeparateLogging; } @Override public void setForcingSeparateLogging( boolean forcingSeparateLogging ) { this.forcingSeparateLogging = forcingSeparateLogging; } @Override public void snap( MetricsInterface metric, long... value ) { snap( metric, null, value ); } @Override public void snap( MetricsInterface metric, String subject, long... value ) { if ( !isGatheringMetrics() ) { return; } String key = MetricsSnapshot.getKey( metric, subject ); Map<String, MetricsSnapshotInterface> metricsMap = null; MetricsSnapshotInterface snapshot = null; Queue<MetricsSnapshotInterface> metricsList = null; switch ( metric.getType() ) { case MAX: // Calculate and store the maximum value for this metric // if ( value.length != 1 ) { break; // ignore } metricsMap = metricsRegistry.getSnapshotMap( logChannelId ); snapshot = metricsMap.get( key ); if ( snapshot != null ) { if ( value[0] > snapshot.getValue() ) { snapshot.setValue( value[0] ); snapshot.setDate( new Date() ); } } else { snapshot = new MetricsSnapshot( MetricsSnapshotType.MAX, metric, subject, value[0], logChannelId ); metricsMap.put( key, snapshot ); } break; case MIN: // Calculate and store the minimum value for this metric // if ( value.length != 1 ) { break; // ignore } metricsMap = metricsRegistry.getSnapshotMap( logChannelId ); snapshot = metricsMap.get( key ); if ( snapshot != null ) { if ( value[0] < snapshot.getValue() ) { snapshot.setValue( value[0] ); snapshot.setDate( new Date() ); } } else { snapshot = new MetricsSnapshot( MetricsSnapshotType.MIN, metric, subject, value[0], logChannelId ); metricsMap.put( key, snapshot ); } break; case SUM: metricsMap = metricsRegistry.getSnapshotMap( logChannelId ); snapshot = metricsMap.get( key ); if ( snapshot != null ) { snapshot.setValue( snapshot.getValue() + value[0] ); } else { snapshot = new MetricsSnapshot( MetricsSnapshotType.SUM, metric, subject, value[0], logChannelId ); metricsMap.put( key, snapshot ); } break; case COUNT: metricsMap = metricsRegistry.getSnapshotMap( logChannelId ); snapshot = metricsMap.get( key ); if ( snapshot != null ) { snapshot.setValue( snapshot.getValue() + 1L ); } else { snapshot = new MetricsSnapshot( MetricsSnapshotType.COUNT, metric, subject, 1L, logChannelId ); metricsMap.put( key, snapshot ); } break; case START: metricsList = metricsRegistry.getSnapshotList( logChannelId ); snapshot = new MetricsSnapshot( MetricsSnapshotType.START, metric, subject, 1L, logChannelId ); metricsList.add( snapshot ); break; case STOP: metricsList = metricsRegistry.getSnapshotList( logChannelId ); snapshot = new MetricsSnapshot( MetricsSnapshotType.STOP, metric, subject, 1L, logChannelId ); metricsList.add( snapshot ); break; default: break; } } @Override public String getFilter() { return filter; } @Override public void setFilter( String filter ) { this.filter = filter; } } 只看到 logChannelId = LoggingRegistry.getInstance().registerLoggingSource( subject ); 没有 public void setLogChannelId(String logChannelId) { this.logChannelId = logChannelId; }
07-30
package com.tongchuang.realtime.mds; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import com.tongchuang.realtime.bean.ULEParamConfig; import com.tongchuang.realtime.util.KafkaUtils; import org.apache.flink.api.common.eventtime.WatermarkStrategy; import org.apache.flink.api.common.state.; import org.apache.flink.api.common.time.Time; import org.apache.flink.api.common.typeinfo.BasicTypeInfo; import org.apache.flink.api.common.typeinfo.TypeHint; import org.apache.flink.api.common.typeinfo.TypeInformation; import org.apache.flink.configuration.Configuration; import org.apache.flink.connector.kafka.source.KafkaSource; import org.apache.flink.connector.kafka.source.enumerator.initializer.OffsetsInitializer; import org.apache.flink.streaming.api.datastream.; import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment; import org.apache.flink.streaming.api.functions.co.KeyedBroadcastProcessFunction; import org.apache.flink.streaming.api.functions.source.RichSourceFunction; import org.apache.flink.util.Collector; import org.apache.flink.util.OutputTag; import java.io.Serializable; import java.sql.; import java.text.ParseException; import java.text.SimpleDateFormat; import java.time.Duration; import java.util.; import java.util.Date; import java.util.concurrent.TimeUnit; public class ULEDataanomalyanalysis { public static void main(String[] args) throws Exception { final StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment(); env.setParallelism(1); env.getConfig().setAutoWatermarkInterval(1000); // Kafka消费者配置 KafkaSource<String> kafkaConsumer = KafkaUtils.getKafkaConsumer( "realdata_minute", "minutedata_uledataanomalyanalysis", OffsetsInitializer.latest() ); // 修改Watermark策略 DataStreamSource<String> kafkaDS = env.fromSource( kafkaConsumer, WatermarkStrategy.<String>forBoundedOutOfOrderness(Duration.ofMinutes(1)) .withTimestampAssigner((event, timestamp) -> { try { JSONObject json = JSON.parseObject(event); return new SimpleDateFormat("yyyy-MM-dd HH:mm").parse(json.getString("times")).getTime(); } catch (ParseException e) { return System.currentTimeMillis(); } }), "realdata_uledataanomalyanalysis" ); kafkaDS.print("分钟数据流"); // 解析JSON并拆分tag SingleOutputStreamOperator<JSONObject> splitStream = kafkaDS .map(JSON::parseObject) .returns(TypeInformation.of(JSONObject.class)) .flatMap((JSONObject value, Collector<JSONObject> out) -> { JSONObject data = value.getJSONObject("datas"); String time = value.getString("times"); for (String tag : data.keySet()) { JSONObject tagData = data.getJSONObject(tag); JSONObject newObj = new JSONObject(); newObj.put("time", time); newObj.put("tag", tag); newObj.put("ontime", tagData.getDouble("ontime")); newObj.put("avg", tagData.getDouble("avg")); out.collect(newObj); } }) .returns(TypeInformation.of(JSONObject.class)) .name("Split-By-Tag"); // 配置数据源 DataStream<ConfigCollection> configDataStream = env .addSource(new MysqlConfigSource()) .setParallelism(1) .filter(Objects::nonNull) .name("Config-Source"); // 标签值数据流 DataStream<Map<String, Object>> tagValueStream = splitStream .map(json -> { Map<String, Object> valueMap = new HashMap<>(); valueMap.put("tag", json.getString("tag")); valueMap.put("value", "436887485805570949".equals(json.getString("datatype")) ? json.getDouble("ontime") : json.getDouble("avg")); return valueMap; }) .returns(new TypeHint<Map<String, Object>>() {}) .name("Tag-Value-Stream"); // 合并配置流和标签值流 DataStream<Object> broadcastStream = configDataStream .map(config -> (Object) config) .returns(TypeInformation.of(Object.class)) .union( tagValueStream.map(tagValue -> (Object) tagValue) .returns(TypeInformation.of(Object.class)) ); // 广播流 BroadcastStream<Object> finalBroadcastStream = broadcastStream .broadcast(Descriptors.configStateDescriptor, Descriptors.tagValuesDescriptor); // 按tag分组 KeyedStream<JSONObject, String> keyedStream = splitStream .keyBy(json -> json.getString("tag")); BroadcastConnectedStream<JSONObject, Object> connectedStream = keyedStream.connect(finalBroadcastStream); // 异常检测处理 SingleOutputStreamOperator<JSONObject> anomalyStream = connectedStream .process(new OptimizedAnomalyDetectionFunction()) .name("Anomaly-Detection"); anomalyStream.print("异常检测结果"); // 离线检测结果侧输出 DataStream<String> offlineCheckStream = anomalyStream.getSideOutput(OptimizedAnomalyDetectionFunction.OFFLINE_CHECK_TAG); offlineCheckStream.print("离线检测结果"); env.execute("uledataanomalyanalysis"); } // 配置集合类 public static class ConfigCollection implements Serializable { private static final long serialVersionUID = 1L; public final Map<String, List<ULEParamConfig>> tagToConfigs; public final Map<String, ULEParamConfig> encodeToConfig; public final Set<String> allTags; public final long checkpointTime; public ConfigCollection(Map<String, List<ULEParamConfig>> tagToConfigs, Map<String, ULEParamConfig> encodeToConfig) { this.tagToConfigs = new HashMap<>(tagToConfigs); this.encodeToConfig = new HashMap<>(encodeToConfig); this.allTags = new HashSet<>(tagToConfigs.keySet()); this.checkpointTime = System.currentTimeMillis(); } } // MySQL配置源 public static class MysqlConfigSource extends RichSourceFunction<ConfigCollection> { private volatile boolean isRunning = true; private final long interval = TimeUnit.MINUTES.toMillis(5); @Override public void run(SourceContext<ConfigCollection> ctx) throws Exception { while (isRunning) { ConfigCollection newConfig = loadParams(); if (newConfig != null) { ctx.collect(newConfig); System.out.println("配置加载完成,检查点时间: " + new Date(newConfig.checkpointTime)); } else { System.out.println("配置加载失败"); } Thread.sleep(interval); } } private ConfigCollection loadParams() { Map<String, List<ULEParamConfig>> tagToConfigs = new HashMap<>(5000); Map<String, ULEParamConfig> encodeToConfig = new HashMap<>(5000); String url = "jdbc:mysql://10.51.37.73:3306/eps?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8"; String user = "root"; String password = "6CKIm5jDVsLrahSw"; String query = "SELECT F_tag AS tag, F_enCode AS encode, F_dataTypes AS datatype, " + "F_isConstantValue AS constantvalue, F_isOnline AS isonline, " + "F_isSync AS issync, F_syncParaEnCode AS syncparaencode, " + "F_isZero AS iszero, F_isHigh AS ishigh, F_highThreshold AS highthreshold, " + "F_isLow AS islow, F_lowThreshold AS lowthreshold, F_duration AS duration " + "FROM t_equipmentparameter " + "WHERE F_enabledmark = '1' AND (F_isConstantValue='1' OR F_isZero='1' " + "OR F_isHigh='1' OR F_isLow='1' OR F_isOnline='1' OR F_isSync='1')"; try (Connection conn = DriverManager.getConnection(url, user, password); Statement stmt = conn.createStatement(); ResultSet rs = stmt.executeQuery(query)) { while (rs.next()) { ULEParamConfig config = new ULEParamConfig(); config.tag = rs.getString("tag"); config.encode = rs.getString("encode"); config.datatype = rs.getString("datatype"); config.constantvalue = rs.getInt("constantvalue"); config.iszero = rs.getInt("iszero"); config.ishigh = rs.getInt("ishigh"); config.highthreshold = rs.getDouble("highthreshold"); config.islow = rs.getInt("islow"); config.lowthreshold = rs.getDouble("lowthreshold"); config.duration = rs.getLong("duration"); config.isonline = rs.getInt("isonline"); config.issync = rs.getInt("issync"); config.syncparaencode = rs.getString("syncparaencode"); if (config.encode == null || config.encode.isEmpty()) { System.err.println("忽略无效配置: 空encode"); continue; } String tag = config.tag; tagToConfigs.computeIfAbsent(tag, k -> new ArrayList<>(10)).add(config); encodeToConfig.put(config.encode, config); } System.out.println("加载配置: " + encodeToConfig.size() + " 个参数"); return new ConfigCollection(tagToConfigs, encodeToConfig); } catch (SQLException e) { System.err.println("加载参数配置错误:"); e.printStackTrace(); return null; } } @Override public void cancel() { isRunning = false; } } // 状态描述符 public static class Descriptors { public static final MapStateDescriptor<Void, ConfigCollection> configStateDescriptor = new MapStateDescriptor<>( "configState", TypeInformation.of(Void.class), TypeInformation.of(ConfigCollection.class) ); public static final MapStateDescriptor<String, Double> tagValuesDescriptor = new MapStateDescriptor<>( "tagValuesBroadcastState", BasicTypeInfo.STRING_TYPE_INFO, BasicTypeInfo.DOUBLE_TYPE_INFO ); } // 重构后的异常检测函数(重点修复离线检测) public static class OptimizedAnomalyDetectionFunction extends KeyedBroadcastProcessFunction<String, JSONObject, Object, JSONObject> { public static final OutputTag<String> OFFLINE_CHECK_TAG = new OutputTag<String>("offline-check"){}; // 状态管理 private transient MapState<String, AnomalyState> stateMap; private transient MapState<String, Long> lastDataTimeMap; private transient MapState<String, Long> lastUpdateTimeState; // 最后更新时间(处理时间) private transient MapState<String, Long> processingTimerState; // 处理时间定时器状态 private transient SimpleDateFormat timeFormat; private transient long lastSyncLogTime = 0; @Override public void open(Configuration parameters) { StateTtlConfig ttlConfig = StateTtlConfig.newBuilder(Time.days(30)) .setUpdateType(StateTtlConfig.UpdateType.OnCreateAndWrite) .setStateVisibility(StateTtlConfig.StateVisibility.NeverReturnExpired) .cleanupFullSnapshot() .build(); // 异常状态存储 MapStateDescriptor<String, AnomalyState> stateDesc = new MapStateDescriptor<>( "anomalyState", BasicTypeInfo.STRING_TYPE_INFO, TypeInformation.of(AnomalyState.class) ); stateDesc.enableTimeToLive(ttlConfig); stateMap = getRuntimeContext().getMapState(stateDesc); // 最后数据时间存储(事件时间) MapStateDescriptor<String, Long> timeDesc = new MapStateDescriptor<>( "lastDataTimeState", BasicTypeInfo.STRING_TYPE_INFO, BasicTypeInfo.LONG_TYPE_INFO ); timeDesc.enableTimeToLive(ttlConfig); lastDataTimeMap = getRuntimeContext().getMapState(timeDesc); // 最后更新时间状态(处理时间) MapStateDescriptor<String, Long> updateTimeDesc = new MapStateDescriptor<>( "lastUpdateTimeState", BasicTypeInfo.STRING_TYPE_INFO, BasicTypeInfo.LONG_TYPE_INFO ); updateTimeDesc.enableTimeToLive(ttlConfig); lastUpdateTimeState = getRuntimeContext().getMapState(updateTimeDesc); // 处理时间定时器状态 MapStateDescriptor<String, Long> timerDesc = new MapStateDescriptor<>( "processingTimerState", BasicTypeInfo.STRING_TYPE_INFO, BasicTypeInfo.LONG_TYPE_INFO ); timerDesc.enableTimeToLive(ttlConfig); processingTimerState = getRuntimeContext().getMapState(timerDesc); timeFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm"); } @Override public void processElement(JSONObject data, ReadOnlyContext ctx, Collector<JSONObject> out) throws Exception { String tag = ctx.getCurrentKey(); String timeStr = data.getString("time"); long eventTime = timeFormat.parse(timeStr).getTime(); long currentProcessingTime = System.currentTimeMillis(); // 更新最后数据时间(事件时间) lastDataTimeMap.put(tag, eventTime); // 更新最后处理时间(关键修改) lastUpdateTimeState.put(tag, currentProcessingTime); // 取消现有定时器(如有) Long existingTimer = processingTimerState.get(tag); if (existingTimer != null) { ctx.timerService().deleteProcessingTimeTimer(existingTimer); } // 获取广播配置 ConfigCollection configCollection = getBroadcastConfig(ctx); if (configCollection == null) return; List<ULEParamConfig> configs = configCollection.tagToConfigs.get(tag); if (configs == null || configs.isEmpty()) return; // 清理无效状态 List<String> keysToRemove = new ArrayList<>(); for (String encode : stateMap.keys()) { boolean found = false; for (ULEParamConfig cfg : configs) { if (cfg.encode.equals(encode)) { found = true; break; } } if (!found) { System.out.println("清理过期状态: " + encode); keysToRemove.add(encode); } } for (String encode : keysToRemove) { stateMap.remove(encode); } double value = 0; boolean valueSet = false; // 重置离线状态(数据到达表示恢复) for (ULEParamConfig config : configs) { if (config.isonline == 1) { AnomalyState state = getOrCreateState(config.encode); AnomalyStatus status = state.getStatus(6); if (status.reported) { reportAnomaly(6, 0, 0.0, timeStr, config, out); status.reset(); stateMap.put(config.encode, state); System.out.println("离线恢复: tag=" + tag + ", encode=" + config.encode); } } } // 注册新定时器(使用处理时间) long minDuration = getMinDuration(configs); if (minDuration != Long.MAX_VALUE) { long nextCheckTime = currentProcessingTime + minDuration * 60 * 1000; ctx.timerService().registerProcessingTimeTimer(nextCheckTime); processingTimerState.put(tag, nextCheckTime); } // 遍历配置项进行异常检测 for (ULEParamConfig config : configs) { if (!valueSet) { value = "436887485805570949".equals(config.datatype) ? data.getDouble("ontime") : data.getDouble("avg"); valueSet = true; } AnomalyState state = getOrCreateState(config.encode); // 处理异常类型 checkConstantValueAnomaly(config, value, timeStr, state, out); // 1. 恒值检测 checkZeroValueAnomaly(config, value, timeStr, state, out); // 2. 零值检测 checkThresholdAnomaly(config, value, timeStr, state, out); // 3. 上阈值, 4. 下阈值 checkSyncAnomaly(config, value, timeStr, state, configCollection, ctx, out); // 5. 同步检测 stateMap.put(config.encode, state); } } @Override public void onTimer(long timestamp, OnTimerContext ctx, Collector<JSONObject> out) throws Exception { String tag = ctx.getCurrentKey(); long currentTime = System.currentTimeMillis(); ConfigCollection configCollection = getBroadcastConfig(ctx); if (configCollection == null) return; List<ULEParamConfig> configs = configCollection.tagToConfigs.get(tag); if (configs == null) return; // 获取标签最后更新时间 Long lastUpdateTime = lastUpdateTimeState.get(tag); boolean hasOnlineConfig = false; long minDuration = Long.MAX_VALUE; for (ULEParamConfig config : configs) { if (config.isonline == 1) { hasOnlineConfig = true; minDuration = Math.min(minDuration, config.duration); AnomalyState state = getOrCreateState(config.encode); AnomalyStatus status = state.getStatus(6); // 检查是否超时 boolean isOffline = false; if (lastUpdateTime == null) { // 从未收到数据 Long initTime = state.getInitTime(); if (initTime == null) { initTime = configCollection.checkpointTime; state.setInitTime(initTime); } isOffline = (currentTime - initTime) >= config.duration * 60 * 1000; } else { // 已有数据但超时 isOffline = (currentTime - lastUpdateTime) >= config.duration * 60 * 1000; } if (isOffline) { if (!status.reported) { String triggerTime = timeFormat.format(new Date(currentTime)); reportAnomaly(6, 1, 0.0, triggerTime, config, out); status.reported = true; // 侧输出流日志 String message = lastUpdateTime == null ? String.format("离线异常(从未收到数据): tag=%s, encode=%s, 阈值=%d分钟", config.tag, config.encode, config.duration) : String.format("离线异常: tag=%s, encode=%s, 最后更新时间=%s, 当前时间=%s, 时间差=%.1f分钟 (阈值=%d分钟)", config.tag, config.encode, timeFormat.format(new Date(lastUpdateTime)), triggerTime, (currentTime - lastUpdateTime) / 60000.0, config.duration); ctx.output(OFFLINE_CHECK_TAG, message); System.out.println(message); } } else if (status.reported) { // 恢复在线状态 reportAnomaly(6, 0, 0.0, timeFormat.format(new Date()), config, out); status.reset(); System.out.println("离线恢复: tag=" + tag + ", encode=" + config.encode); } stateMap.put(config.encode, state); } } // 重新注册定时器(每分钟检查一次) if (hasOnlineConfig) { long nextCheckTime = currentTime + TimeUnit.MINUTES.toMillis(1); ctx.timerService().registerProcessingTimeTimer(nextCheckTime); processingTimerState.put(tag, nextCheckTime); } } // 辅助方法:获取最小duration private long getMinDuration(List<ULEParamConfig> configs) { long minDuration = Long.MAX_VALUE; for (ULEParamConfig config : configs) { if (config.isonline == 1) { minDuration = Math.min(minDuration, config.duration); } } return minDuration; } // 恒值检测 - 异常类型1 private void checkConstantValueAnomaly(ULEParamConfig config, double currentValue, String timeStr, AnomalyState state, Collector<JSONObject> out) { if (config.constantvalue != 1) return; try { AnomalyStatus status = state.getStatus(1); long durationThreshold = config.duration * 60 * 1000; Date timestamp = timeFormat.parse(timeStr); if (status.lastValue == null) { status.lastValue = currentValue; status.lastChangeTime = timestamp; return; } if (Math.abs(currentValue - status.lastValue) > 0.001) { status.lastValue = currentValue; status.lastChangeTime = timestamp; if (status.reported) { reportAnomaly(1, 0, currentValue, timeStr, config, out); } status.reset(); return; } long elapsed = timestamp.getTime() - status.lastChangeTime.getTime(); if (elapsed > durationThreshold) { if (!status.reported) { reportAnomaly(1, 1, currentValue, timeStr, config, out); status.reported = true; } } } catch (Exception e) { System.err.println("恒值检测错误: " + config.encode + " - " + e.getMessage()); } } // 零值检测 - 异常类型2 private void checkZeroValueAnomaly(ULEParamConfig config, double currentValue, String timeStr, AnomalyState state, Collector<JSONObject> out) { if (config.iszero != 1) return; try { AnomalyStatus status = state.getStatus(2); Date timestamp = timeFormat.parse(timeStr); boolean isZero = Math.abs(currentValue) < 0.001; if (isZero) { if (status.startTime == null) { status.startTime = timestamp; } else if (!status.reported) { long elapsed = timestamp.getTime() - status.startTime.getTime(); if (elapsed >= config.duration * 60 * 1000) { reportAnomaly(2, 1, currentValue, timeStr, config, out); status.reported = true; } } } else { if (status.reported) { reportAnomaly(2, 0, currentValue, timeStr, config, out); status.reset(); } else if (status.startTime != null) { status.startTime = null; } } } catch (Exception e) { System.err.println("零值检测错误: " + config.encode + " - " + e.getMessage()); } } // 阈值检测 - 异常类型3(上阈值)和4(下阈值) private void checkThresholdAnomaly(ULEParamConfig config, double currentValue, String timeStr, AnomalyState state, Collector<JSONObject> out) { try { if (config.ishigh == 1) { AnomalyStatus highStatus = state.getStatus(3); processThresholdAnomaly(highStatus, currentValue, timeStr, currentValue > config.highthreshold, config, 3, out); } if (config.islow == 1) { AnomalyStatus lowStatus = state.getStatus(4); processThresholdAnomaly(lowStatus, currentValue, timeStr, currentValue < config.lowthreshold, config, 4, out); } } catch (Exception e) { System.err.println("阈值检测错误: " + config.encode + " - " + e.getMessage()); } } private void processThresholdAnomaly(AnomalyStatus status, double currentValue, String timeStr, boolean isAnomaly, ULEParamConfig config, int anomalyType, Collector<JSONObject> out) { try { Date timestamp = timeFormat.parse(timeStr); if (isAnomaly) { if (status.startTime == null) { status.startTime = timestamp; } else if (!status.reported) { long elapsed = timestamp.getTime() - status.startTime.getTime(); if (elapsed >= config.duration * 60 * 1000) { reportAnomaly(anomalyType, 1, currentValue, timeStr, config, out); status.reported = true; } } } else { if (status.reported) { reportAnomaly(anomalyType, 0, currentValue, timeStr, config, out); status.reset(); } else if (status.startTime != null) { status.startTime = null; } } } catch (Exception e) { System.err.println("阈值处理错误:极速版 " + config.encode + " - " + e.getMessage()); } } // 同步检测 - 异常类型5(优化日志输出) private void checkSyncAnomaly(ULEParamConfig config, double currentValue, String timeStr, AnomalyState state, ConfigCollection configCollection, ReadOnlyContext ctx, Collector<JSONObject> out) { if (config.issync != 1 || config.syncparaencode == null || config.syncparaencode.isEmpty()) { return; } try { // 通过encode获取关联配置 ULEParamConfig relatedConfig = configCollection.encodeToConfig.get(config.syncparaencode); if (relatedConfig == null) { if (System.currentTimeMillis() - lastSyncLogTime > 60000) { System.out.println("同步检测错误: 未找到关联配置, encode=" + config.syncparaencode); lastSyncLogTime = System.currentTimeMillis(); } return; } // 获取关联配置的tag String relatedTag = relatedConfig.tag; if (relatedTag == null || relatedTag.isEmpty()) { if (System.currentTimeMillis() - lastSyncLogTime > 60000) { System.out.println("同步检测错误: 关联配置没有tag, encode=" + config.syncparaencode); lastSyncLogTime = System.currentTimeMillis(); } return; } // 从广播状态获取关联值 ReadOnlyBroadcastState<String, Double> tagValuesState = ctx.getBroadcastState(Descriptors.tagValuesDescriptor); Double relatedValue = tagValuesState.get(relatedTag); if (relatedValue == null) { if (System.currentTimeMillis() - lastSyncLogTime > 60000) { // 优化日志:添加当前标签信息 System.out.printf("同步检测警告: 关联值未初始化 [主标签=%s(%s), 关联标签=%s(%s)]%n", config.tag, config.encode, relatedTag, config.syncparaencode); lastSyncLogTime = System.currentTimeMillis(); } return; } // 同步检测逻辑 AnomalyStatus status = state.getStatus(5); Date timestamp = timeFormat.parse(timeStr); // 业务逻辑:当前值接近1且关联值接近0时异常 boolean isAnomaly = (currentValue >= 0.99) && (Math.abs(relatedValue) < 0.01); // 日志记录 if (System.currentTimeMillis() - lastSyncLogTime > 60000) { System.out.printf("同步检测: %s (%.4f) vs %s (%.4f) -> %b%n", config.tag, currentValue, relatedTag, relatedValue, isAnomaly); lastSyncLogTime = System.currentTimeMillis(); } // 处理异常状态 if (isAnomaly) { if (status.startTime == null) { status.startTime = timestamp; } else if (!status.reported) { long elapsed = timestamp.getTime() - status.startTime.getTime(); if (elapsed >= config.duration * 60 * 1000) { reportAnomaly(5, 1, currentValue, timeStr, config, out); status.reported = true; } } } else { if (status.reported) { reportAnomaly(5, 0, currentValue, timeStr, config, out); status.reset(); } else if (status.startTime != null) { status.startTime = null; } } } catch (ParseException e) { System.err.println("同步检测时间解析错误: " + config.encode + " - " + e.getMessage()); } catch (Exception e) { System.err.println("同步检测错误: " + config.encode + " - " + e.getMessage()); } } // 报告异常(添加详细日志) private void reportAnomaly(int anomalyType, int statusFlag, double value, String time, ULEParamConfig config, Collector<JSONObject> out) { JSONObject event = new JSONObject(); event.put("tag", config.tag); event.put("paracode", config.encode); event.put("abnormaltype", anomalyType); event.put("statusflag", statusFlag); event.put("datavalue", value); event.put("triggertime", time); out.collect(event); // 添加详细日志输出 String statusDesc = statusFlag == 1 ? "异常开始" : "异常结束"; System.out.printf("报告异常: 类型=%d, 状态=%s, 标签=%s, 编码=%s, 时间=%s%n", anomalyType, statusDesc, config.tag, config.encode, time); } @Override public void processBroadcastElement(Object broadcastElement, Context ctx, Collector<JSONObject> out) throws Exception { // 处理配置更新 if (broadcastElement instanceof ConfigCollection) { ConfigCollection newConfig = (ConfigCollection) broadcastElement; BroadcastState<Void, ConfigCollection> configState = ctx.getBroadcastState(Descriptors.configStateDescriptor); // 获取旧配置 ConfigCollection oldConfig = configState.get(null); // 处理配置变更:清理不再启用的报警 if (oldConfig != null) { for (Map.Entry<String, ULEParamConfig> entry : oldConfig.encodeToConfig.entrySet()) { String encode = entry.getKey(); ULEParamConfig oldCfg = entry.getValue(); ULEParamConfig newCfg = newConfig.encodeToConfig.get(encode); if (newCfg == null || !isAlarmEnabled(newCfg, oldCfg)) { // 发送恢复事件 sendRecoveryEvents(encode, oldCfg, ctx, out); } } } // 更新广播状态 configState.put(null, newConfig); System.out.println("广播配置更新完成, 配置项: " + newConfig.encodeToConfig); } // 处理标签值更新 else if (broadcastElement instanceof Map) { @SuppressWarnings("unchecked") Map<String, Object> tagValue = (Map<String, Object>) broadcastElement; String tag = (String) tagValue.get("tag"); Double value = (Double) tagValue.get("value"); if (tag != null && value != null) { BroadcastState<String, Double> tagValuesState = ctx.getBroadcastState(Descriptors.tagValuesDescriptor); tagValuesState.put(tag, value); } } } // 检查报警是否启用 private boolean isAlarmEnabled(ULEParamConfig newCfg, ULEParamConfig oldCfg) { return (oldCfg.constantvalue == 1 && newCfg.constantvalue == 1) || (oldCfg.iszero == 1 && newCfg.iszero == 1) || (oldCfg.ishigh == 1 && newCfg.ishigh == 1) || (oldCfg.islow == 1 && newCfg.islow == 1) || (oldCfg.isonline == 1 && newCfg.isonline == 1) || (oldCfg.issync == 1 && newCfg.issync == 1); } // 发送恢复事件 private void sendRecoveryEvents(String encode, ULEParamConfig config, Context ctx, Collector<JSONObject> out) { try { AnomalyState state = stateMap.get(encode); if (state == null) return; // 遍历所有可能的报警类型 for (int type = 1; type <= 6; type++) { AnomalyStatus status = state.getStatus(type); if (status.reported) { JSONObject recoveryEvent = new JSONObject(); recoveryEvent.put("tag", config.tag); recoveryEvent.put("paracode", config.encode); recoveryEvent.put("abnormaltype", type); recoveryEvent.put("statusflag", 0); // 恢复事件 recoveryEvent.put("datavalue", 0.0); recoveryEvent.put("triggertime", timeFormat.format(new Date())); out.collect(recoveryEvent); System.out.println("发送恢复事件: 类型=" + type + ", 标签=" + config.tag); status.reset(); } } // 更新状态 stateMap.put(encode, state); } catch (Exception e) { System.err.println("发送恢复事件失败: " + e.getMessage()); } } // 辅助方法 private ConfigCollection getBroadcastConfig(ReadOnlyContext ctx) throws Exception { return ctx.getBroadcastState(Descriptors.configStateDescriptor).get(null); } private AnomalyState getOrCreateState(String encode) throws Exception { AnomalyState state = stateMap.get(encode); if (state == null) { state = new AnomalyState(); } return state; } } // 异常状态类(新增initTime字段) public static class AnomalyState implements Serializable { private static final long serialVersionUID = 1L; private final Map<Integer, AnomalyStatus> statusMap = new HashMap<>(); private Long initTime; // 初始化时间(用于从未收到数据的检测) public AnomalyStatus getStatus(int type) { return statusMap.computeIfAbsent(type, k -> new AnomalyStatus()); } public Long getInitTime() { return initTime; } public void setInitTime(Long initTime) { this.initTime = initTime; } } // 异常状态详情(保持不变) public static class AnomalyStatus implements Serializable { private static final long serialVersionUID = 1L; public Date startTime; // 异常开始时间 public Double lastValue; // 用于恒值检测 public Date lastChangeTime; // 值最后变化时间 public boolean reported; // 是否已报告 public void reset() { startTime = null; lastValue = null; lastChangeTime = null; reported = false; } } } 运行日志为:“C:\Program Files (x86)\Java\jdk1.8.0_102\bin\java.exe” -agentlib:jdwp=transport=dt_socket,address=127.0.0.1:24763,suspend=y,server=n -javaagent:C:\Users\Administrator\AppData\Local\JetBrains\IntelliJIdea2021.2\captureAgent\debugger-agent.jar=file:/C:/Users/Administrator/AppData/Local/Temp/capture.props -Dfile.encoding=UTF-8 -classpath C:\Users\Administrator\AppData\Local\Temp\classpath1582414257.jar com.tongchuang.realtime.mds.ULEDataanomalyanalysis 已连接到目标 VM, 地址: ‘‘127.0.0.1:24763’,传输: ‘套接字’’ SLF4J: Class path contains multiple SLF4J bindings. SLF4J: Found binding in [jar:file:/F:/flink/flinkmaven/repository/org/apache/logging/log4j/log4j-slf4j-impl/2.10.0/log4j-slf4j-impl-2.10.0.jar!/org/slf4j/impl/StaticLoggerBinder.class] SLF4J: Found binding in [jar:file:/F:/flink/flinkmaven/repository/org/slf4j/slf4j-log4j12/1.7.25/slf4j-log4j12-1.7.25.jar!/org/slf4j/impl/StaticLoggerBinder.class] SLF4J: See http://www.slf4j.org/codes.html#multiple_bindings for an explanation. SLF4J: Actual binding is of type [org.apache.logging.slf4j.Log4jLoggerFactory] 加载配置: 30 个参数 配置加载完成,检查点时间: Wed Aug 06 08:59:30 CST 2025 广播配置更新完成, 配置项: {EP000015=ULEParamConfig(tag=DA-LT-6BT005, encode=EP000015, datatype=436887289021410181, constantvalue=1, iszero=1, isonline=1, issync=0, syncparaencode=, ishigh=1, highthreshold=10000.0, islow=1, lowthreshold=10.0, duration=3), EP000014=ULEParamConfig(tag=DA_DB195_RH_R_0281, encode=EP000014, datatype=436887289021410181, constantvalue=1, iszero=1, isonline=1, issync=0, syncparaencode=, ishigh=1, highthreshold=10000.0, islow=1, lowthreshold=10.0, duration=3), EP000013=ULEParamConfig(tag=DA-LT-6BT008, encode=EP000013, datatype=436887289021410181, constantvalue=1, iszero=1, isonline=1, issync=0, syncparaencode=, ishigh=1, highthreshold=10000.0, islow=1, lowthreshold=10.0, duration=3), EP000012=ULEParamConfig(tag=DA-LT-6BT004, encode=EP000012, datatype=436887289021410181, constantvalue=1, iszero=1, isonline=1, issync=0, syncparaencode=, ishigh=1, highthreshold=10000.0, islow=1, lowthreshold=10.0, duration=3), EP000011=ULEParamConfig(tag=DA-LT-6BT005, encode=EP000011, datatype=436887289021410181, constantvalue=1, iszero=1, isonline=1, issync=0, syncparaencode=, ishigh=1, highthreshold=10000.0, islow=1, lowthreshold=10.0, duration=3), EP000010=ULEParamConfig(tag=DA-LT-6BT001, encode=EP000010, datatype=436887289021410181, constantvalue=1, iszero=1, isonline=1, issync=0, syncparaencode=, ishigh=1, highthreshold=10000.0, islow=1, lowthreshold=10.0, duration=3), EP100013=ULEParamConfig(tag=DA-LT-6BT008, encode=EP100013, datatype=436887289021410181, constantvalue=1, iszero=1, isonline=1, issync=0, syncparaencode=, ishigh=1, highthreshold=10000.0, islow=1, lowthreshold=10.0, duration=3), EP100014=ULEParamConfig(tag=DA_DB195_RH_R_0281, encode=EP100014, datatype=436887289021410181, constantvalue=1, iszero=1, isonline=1, issync=0, syncparaencode=, ishigh=1, highthreshold=10000.0, islow=1, lowthreshold=10.0, duration=3), EP000019=ULEParamConfig(tag=DA-LT-5BT0005, encode=EP000019, datatype=436887248101780357, constantvalue=1, iszero=1, isonline=1, issync=0, syncparaencode=, ishigh=1, highthreshold=10000.0, islow=1, lowthreshold=10.0, duration=3), EP000018=ULEParamConfig(tag=DA-LT-4BT0004, encode=EP000018, datatype=436887289021410181, constantvalue=1, iszero=1, isonline=1, issync=0, syncparaencode=, ishigh=1, highthreshold=10000.0, islow=1, lowthreshold=10.0, duration=3), EP100010=ULEParamConfig(tag=DA-LT-6BT001, encode=EP100010, datatype=436887289021410181, constantvalue=1, iszero=1, isonline=1, issync=0, syncparaencode=, ishigh=1, highthreshold=10000.0, islow=1, lowthreshold=10.0, duration=3), EP000017=ULEParamConfig(tag=DA-LT-4BT0005, encode=EP000017, datatype=436887289021410181, constantvalue=1, iszero=1, isonline=1, issync=0, syncparaencode=, ishigh=1, highthreshold=10000.0, islow=1, lowthreshold=10.0, duration=3), EP100011=ULEParamConfig(tag=DA-LT-6BT005, encode=EP100011, datatype=436887289021410181, constantvalue=1, iszero=1, isonline=1, issync=0, syncparaencode=, ishigh=1, highthreshold=10000.0, islow=1, lowthreshold=10.0, duration=3), EP000016=ULEParamConfig(tag=DA-LT-6BT004, encode=EP000016, datatype=436887289021410181, constantvalue=1, iszero=1, isonline=1, issync=0, syncparaencode=, ishigh=1, highthreshold=10000.0, islow=1, lowthreshold=10.0, duration=3), EP100012=ULEParamConfig(tag=DA-LT-6BT004, encode=EP100012, datatype=436887289021410181, constantvalue=1, iszero=1, isonline=1, issync=0, syncparaencode=, ishigh=1, highthreshold=10000.0, islow=1, lowthreshold=10.0, duration=3), EP000004=ULEParamConfig(tag=DA-LT-4BT0004, encode=EP000004, datatype=436887289021410181, constantvalue=1, iszero=1, isonline=1, issync=0, syncparaencode=, ishigh=1, highthreshold=10000.0, islow=1, lowthreshold=10.0, duration=3), EP000003=ULEParamConfig(tag=DA-LT-4BT0008, encode=EP000003, datatype=436887289021410181, constantvalue=1, iszero=1, isonline=1, issync=0, syncparaencode=, ishigh=1, highthreshold=10000.0, islow=1, lowthreshold=10.0, duration=3), EP000002=ULEParamConfig(tag=DA-LT-4BT0001, encode=EP000002, datatype=436887289021410181, constantvalue=1, iszero=1, isonline=1, issync=0, syncparaencode=, ishigh=1, highthreshold=10000.0, islow=1, lowthreshold=10.0, duration=3), EP000001=ULEParamConfig(tag=DA-DB195-RH-B-0201, encode=EP000001, datatype=436887485805570949, constantvalue=1, iszero=1, isonline=1, issync=1, syncparaencode=EP000022, ishigh=1, highthreshold=10000.0, islow=1, lowthreshold=10.0, duration=3), EP000022=ULEParamConfig(tag=DA-LT-4BT0007, encode=EP000022, datatype=436887289021410181, constantvalue=1, iszero=1, isonline=1, issync=0, syncparaencode=, ishigh=1, highthreshold=10000.0, islow=1, lowthreshold=10.0, duration=3), EP000021=ULEParamConfig(tag=DA-LT-6BT008, encode=EP000021, datatype=436887289021410181, constantvalue=1, iszero=1, isonline=1, issync=0, syncparaencode=, ishigh=1, highthreshold=10000.0, islow=1, lowthreshold=10.0, duration=3), EP100007=ULEParamConfig(tag=DA-LT-5BT0005, encode=EP100007, datatype=436887289021410181, constantvalue=1, iszero=1, isonline=1, issync=0, syncparaencode=, ishigh=1, highthreshold=10000.0, islow=1, lowthreshold=10.0, duration=3), EP000020=ULEParamConfig(tag=DA-LT-6BT005, encode=EP000020, datatype=436887248101780357, constantvalue=1, iszero=1, isonline=1, issync=0, syncparaencode=, ishigh=1, highthreshold=10000.0, islow=1, lowthreshold=10.0, duration=3), EP100008=ULEParamConfig(tag=DA-LT-5BT0005, encode=EP100008, datatype=436887289021410181, constantvalue=1, iszero=1, isonline=1, issync=0, syncparaencode=, ishigh=1, highthreshold=10000.0, islow=1, lowthreshold=10.0, duration=3), EP100009=ULEParamConfig(tag=DA-LT-5BT0008, encode=EP100009, datatype=436887289021410181, constantvalue=1, iszero=1, isonline=1, issync=0, syncparaencode=, ishigh=1, highthreshold=10000.0, islow=1, lowthreshold=10.0, duration=3), EP000009=ULEParamConfig(tag=DA-LT-5BT0008, encode=EP000009, datatype=436887289021410181, constantvalue=1, iszero=1, isonline=1, issync=0, syncparaencode=, ishigh=1, highthreshold=10000.0, islow=1, lowthreshold=10.0, duration=3), EP000008=ULEParamConfig(tag=DA-LT-5BT0004, encode=EP000008, datatype=436887289021410181, constantvalue=1, iszero=1, isonline=1, issync=0, syncparaencode=, ishigh=1, highthreshold=10000.0, islow=1, lowthreshold=10.0, duration=3), EP000007=ULEParamConfig(tag=DA-LT-5BT0004, encode=EP000007, datatype=436887289021410181, constantvalue=1, iszero=1, isonline=1, issync=0, syncparaencode=, ishigh=1, highthreshold=10000.0, islow=1, lowthreshold=10.0, duration=3), EP000006=ULEParamConfig(tag=DA-LT-5BT0001, encode=EP000006, datatype=436887289021410181, constantvalue=1, iszero=1, isonline=1, issync=0, syncparaencode=, ishigh=1, highthreshold=10000.0, islow=1, lowthreshold=10.0, duration=3), EP000005=ULEParamConfig(tag=DA-LT-4BT0005, encode=EP000005, datatype=436887289021410181, constantvalue=1, iszero=1, isonline=1, issync=0, syncparaencode=, ishigh=1, highthreshold=10000.0, islow=1, lowthreshold=10.0, duration=3)} 分钟数据流> {“times”:“2025-08-06 08:59”,“datas”:{“DA-LT-5BT0001”:{“ontime”:3064.1802,“avg”:3067.4769,“min”:3020.7207,“max”:3103.9932},“DA-LT-6BT008”:{“ontime”:182.8847,“avg”:181.7926,“min”:180.9806,“max”:182.8847},“DA-LT-5BT0005”:{“ontime”:402.42,“avg”:403.339,“min”:402.42,“max”:404.28},“DA-LT-6BT004”:{“ontime”:1216.4056,“avg”:1216.0174,“min”:1215.5714,“max”:1216.439},“DA-LT-5BT0004”:{“ontime”:1195.0,“avg”:1196.3017,“min”:1194.8,“max”:1197.9},“DA-LT-6BT005”:{“ontime”:401.5108,“avg”:400.6736,“min”:399.7049,“max”:401.5108},“DA-LT-5BT0008”:{“ontime”:184.3,“avg”:185.3683,“min”:184.3,“max”:185.9},“DA-NY-LG1ZL-2-001”:{“ontime”:0.0,“avg”:0.0,“min”:0.0,“max”:0.0},“DA-LT-4BT0008”:{“ontime”:146.1625,“avg”:145.9867,“min”:144.8875,“max”:149.0875},“DB5701P250A00_101”:{“ontime”:1.0,“avg”:1.0,“min”:1.0,“max”:1.0},“DA-LT-4BT0004”:{“ontime”:1209.0,“avg”:1209.0,“min”:1209.0,“max”:1209.0},“DA-LT-6BT001”:{“ontime”:178349.86,“avg”:178462.2957,“min”:176977.33,“max”:179571.25},“DA-NY-LG2ZL-2-003”:{“ontime”:1.0,“avg”:1.0,“min”:1.0,“max”:1.0},“DA-LT-4BT0005”:{“ontime”:316.5625,“avg”:315.4229,“min”:313.6875,“max”:318.6875},“DA_DB195_RH_R_0281”:{“ontime”:333.94,“avg”:345.6455,“min”:307.85,“max”:393.92},“DA-DB195-RH-B-0200”:{“ontime”:1.0,“avg”:1.0,“min”:1.0,“max”:1.0},“DA-DB195-RH-B-0201”:{“ontime”:1.0,“avg”:1.0,“min”:1.0,“max”:1.0},“DA-LT-4BT0001”:{“ontime”:115738.414,“avg”:116529.9705,“min”:114829.37,“max”:117957.84}}} 同步检测警告: 关联值未初始化 [主标签=DA-DB195-RH-B-0201(EP000001), 关联标签=DA-LT-4BT0007(EP000022)] 分钟数据流> {“times”:“2025-08-06 09:00”,“datas”:{“DA-LT-5BT0001”:{“ontime”:3070.2063,“avg”:3069.2265,“min”:3027.1624,“max”:3147.2275},“DA-LT-6BT008”:{“ontime”:181.1966,“avg”:183.026,“min”:181.1966,“max”:185.5542},“DA-LT-5BT0005”:{“ontime”:404.34,“avg”:404.731,“min”:404.28,“max”:405.06},“DA-LT-6BT004”:{“ontime”:1215.5381,“avg”:1215.0252,“min”:1214.4702,“max”:1215.6382},“DA-LT-5BT0004”:{“ontime”:1197.9,“avg”:1199.1767,“min”:1197.9,“max”:1200.3},“DA-LT-6BT005”:{“ontime”:399.8423,“avg”:400.2738,“min”:399.0375,“max”:402.0015},“DA-LT-5BT0008”:{“ontime”:185.94,“avg”:186.4097,“min”:185.94,“max”:186.7},“DA-NY-LG1ZL-2-001”:{“ontime”:0.0,“avg”:0.0,“min”:0.0,“max”:0.0},“DA-LT-4BT0008”:{“ontime”:145.1375,“avg”:145.8042,“min”:144.075,“max”:148.1625},“DB5701P250A00_101”:{“ontime”:1.0,“avg”:1.0,“min”:1.0,“max”:1.0},“DA-LT-4BT0004”:{“ontime”:1209.0,“avg”:1209.0,“min”:1209.0,“max”:1209.0},“DA-LT-6BT001”:{“ontime”:178488.16,“avg”:178323.2988,“min”:177415.97,“max”:179328.48},“DA-LT-4BT0005”:{“ontime”:313.9375,“avg”:315.1292,“min”:313.8125,“max”:317.5625},“DA-NY-LG2ZL-2-003”:{“ontime”:1.0,“avg”:1.0,“min”:1.0,“max”:1.0},“DA_DB195_RH_R_0281”:{“ontime”:334.58,“avg”:337.786,“min”:311.45,“max”:367.58},“DA-DB195-RH-B-0200”:{“ontime”:1.0,“avg”:1.0,“min”:1.0,“max”:1.0},“DA-LT-4BT0001”:{“ontime”:117321.32,“avg”:116257.2892,“min”:115314.13,“max”:117696.305},“DA-DB195-RH-B-0201”:{“ontime”:1.0,“avg”:1.0,“min”:1.0,“max”:1.0}}} 同步检测警告: 关联值未初始化 [主标签=DA-DB195-RH-B-0201(EP000001), 关联标签=DA-LT-4BT0007(EP000022)] 分钟数据流> {“times”:“2025-08-06 09:01”,“datas”:{“DA-LT-5BT0001”:{“ontime”:3094.005,“avg”:3066.9,“min”:3017.5212,“max”:3141.851},“DA-LT-6BT008”:{“ontime”:185.6327,“avg”:186.4932,“min”:185.5542,“max”:187.1049},“DA-LT-5BT0005”:{“ontime”:404.4,“avg”:402.804,“min”:401.04,“max”:404.4},“DA-LT-6BT004”:{“ontime”:1214.5369,“avg”:1213.9847,“min”:1213.4358,“max”:1214.5369},“DA-LT-5BT0004”:{“ontime”:1199.9,“avg”:1200.7833,“min”:1199.9,“max”:1201.6},“DA-LT-6BT005”:{“ontime”:402.1193,“avg”:403.8882,“min”:402.1193,“max”:405.4562},“DA-LT-5BT0008”:{“ontime”:186.56,“avg”:186.504,“min”:184.82,“max”:187.7},“DA-NY-LG1ZL-2-001”:{“ontime”:0.0,“avg”:0.0,“min”:0.0,“max”:0.0},“DA-LT-4BT0008”:{“ontime”:143.8125,“avg”:144.5035,“min”:143.475,“max”:145.3625},“DB5701P250A00_101”:{“ontime”:1.0,“avg”:1.0,“min”:1.0,“max”:1.0},“DA-LT-4BT0004”:{“ontime”:1209.0,“avg”:1209.0,“min”:1209.0,“max”:1209.0},“DA-LT-6BT001”:{“ontime”:178296.28,“avg”:177633.015,“min”:176934.88,“max”:178406.75},“DA-NY-LG2ZL-2-003”:{“ontime”:1.0,“avg”:1.0,“min”:1.0,“max”:1.0},“DA-LT-4BT0005”:{“ontime”:314.3125,“avg”:314.1802,“min”:313.625,“max”:314.9375},“DA_DB195_RH_R_0281”:{“ontime”:336.73,“avg”:335.9752,“min”:293.96,“max”:361.86},“DA-DB195-RH-B-0200”:{“ontime”:1.0,“avg”:1.0,“min”:1.0,“max”:1.0},“DA-LT-4BT0001”:{“ontime”:117313.79,“avg”:116876.3563,“min”:115721.56,“max”:117964.16},“DA-DB195-RH-B-0201”:{“ontime”:1.0,“avg”:1.0,“min”:1.0,“max”:1.0}}} 同步检测警告: 关联值未初始化 [主标签=DA-DB195-RH-B-0201(EP000001), 关联标签=DA-LT-4BT0007(EP000022)] 分钟数据流> {“times”:“2025-08-06 09:02”,“datas”:{“DA-LT-5BT0001”:{“ontime”:3083.3037,“avg”:3060.3783,“min”:3005.7114,“max”:3103.624},“DA-LT-6BT008”:{“ontime”:186.2216,“avg”:186.0545,“min”:185.5346,“max”:186.3983},“DA-LT-5BT0005”:{“ontime”:401.04,“avg”:399.802,“min”:399.3,“max”:401.04},“DA-LT-6BT004”:{“ontime”:1213.4358,“avg”:1212.9452,“min”:1212.4012,“max”:1213.5359},“DA-LT-5BT0004”:{“ontime”:1201.6,“avg”:1203.3484,“min”:1201.6,“max”:1204.9},“DA-LT-6BT005”:{“ontime”:405.5151,“avg”:405.8665,“min”:405.5151,“max”:406.0451},“DA-LT-5BT0008”:{“ontime”:187.56,“avg”:185.292,“min”:183.66,“max”:187.56},“DA-NY-LG1ZL-2-001”:{“ontime”:0.0,“avg”:0.0,“min”:0.0,“max”:0.0},“DA-LT-4BT0008”:{“ontime”:144.925,“avg”:146.3315,“min”:144.9,“max”:149.075},“DB5701P250A00_101”:{“ontime”:1.0,“avg”:1.0,“min”:1.0,“max”:1.0},“DA-LT-4BT0004”:{“ontime”:1209.0,“avg”:1209.0167,“min”:1209.0,“max”:1210.0},“DA-LT-6BT001”:{“ontime”:176681.73,“avg”:177170.773,“min”:176415.9,“max”:178256.67},“DA-NY-LG2ZL-2-003”:{“ontime”:1.0,“avg”:1.0,“min”:1.0,“max”:1.0},“DA-LT-4BT0005”:{“ontime”:314.125,“avg”:313.9531,“min”:312.875,“max”:315.875},“DA_DB195_RH_R_0281”:{“ontime”:322.99,“avg”:335.8693,“min”:312.07,“max”:393.37},“DA-DB195-RH-B-0200”:{“ontime”:1.0,“avg”:1.0,“min”:1.0,“max”:1.0},“DA-LT-4BT0001”:{“ontime”:116846.76,“avg”:117260.9993,“min”:116176.56,“max”:118419.29},“DA-DB195-RH-B-0201”:{“ontime”:1.0,“avg”:1.0,“min”:1.0,“max”:1.0}}} 异常检测结果> {“abnormaltype”:3,“paracode”:“EP100010”,“datavalue”:177170.773,“tag”:“DA-LT-6BT001”,“triggertime”:“2025-08-06 09:02”,“statusflag”:1} 报告异常: 类型=3, 状态=异常开始, 标签=DA-LT-6BT001, 编码=EP100010, 时间=2025-08-06 09:02 异常检测结果> {“abnormaltype”:3,“paracode”:“EP000010”,“datavalue”:177170.773,“tag”:“DA-LT-6BT001”,“triggertime”:“2025-08-06 09:02”,“statusflag”:1} 报告异常: 类型=3, 状态=异常开始, 标签=DA-LT-6BT001, 编码=EP000010, 时间=2025-08-06 09:02 异常检测结果> {“abnormaltype”:3,“paracode”:“EP000002”,“datavalue”:117260.9993,“tag”:“DA-LT-4BT0001”,“triggertime”:“2025-08-06 09:02”,“statusflag”:1} 报告异常: 类型=3, 状态=异常开始, 标签=DA-LT-4BT0001, 编码=EP000002, 时间=2025-08-06 09:02 异常检测结果> {“abnormaltype”:4,“paracode”:“EP000001”,“datavalue”:1.0,“tag”:“DA-DB195-RH-B-0201”,“triggertime”:“2025-08-06 09:02”,“statusflag”:1} 报告异常: 类型=4, 状态=异常开始, 标签=DA-DB195-RH-B-0201, 编码=EP000001, 时间=2025-08-06 09:02 分钟数据流> {“times”:“2025-08-06 09:03”,“datas”:{“DA-LT-5BT0001”:{“ontime”:3027.0603,“avg”:3059.1179,“min”:2999.651,“max”:3113.997},“DA-LT-6BT008”:{“ontime”:186.359,“avg”:185.849,“min”:184.1802,“max”:187.0461},“DA-LT-5BT0005”:{“ontime”:399.78,“avg”:400.624,“min”:399.78,“max”:401.4},“DA-LT-6BT004”:{“ontime”:1212.4347,“avg”:1211.9953,“min”:1211.6671,“max”:1212.468},“DA-LT-5BT0004”:{“ontime”:1204.9,“avg”:1206.05,“min”:1204.9,“max”:1207.0},“DA-LT-6BT005”:{“ontime”:405.8684,“avg”:404.3949,“min”:402.8652,“max”:405.8684},“DA-LT-5BT0008”:{“ontime”:183.7,“avg”:183.3153,“min”:182.34,“max”:184.44},“DA-NY-LG1ZL-2-001”:{“ontime”:0.0,“avg”:0.0,“min”:0.0,“max”:0.0},“DA-LT-4BT0008”:{“ontime”:145.975,“avg”:146.1125,“min”:145.3125,“max”:147.1625},“DB5701P250A00_101”:{“ontime”:1.0,“avg”:1.0,“min”:1.0,“max”:1.0},“DA-LT-4BT0004”:{“ontime”:1209.0,“avg”:1209.15,“min”:1209.0,“max”:1210.0},“DA-LT-6BT001”:{“ontime”:177278.47,“avg”:178191.3243,“min”:177278.47,“max”:179240.23},“DA-LT-4BT0005”:{“ontime”:313.875,“avg”:314.3042,“min”:313.5625,“max”:315.1875},“DA-NY-LG2ZL-2-003”:{“ontime”:1.0,“avg”:1.0,“min”:1.0,“max”:1.0},“DA_DB195_RH_R_0281”:{“ontime”:380.65,“avg”:344.1003,“min”:320.36,“max”:380.65},“DA-DB195-RH-B-0200”:{“ontime”:1.0,“avg”:1.0,“min”:1.0,“max”:1.0},“DA-LT-4BT0001”:{“ontime”:116501.67,“avg”:116541.2536,“min”:115593.18,“max”:117664.38},“DA-DB195-RH-B-0201”:{“ontime”:1.0,“avg”:1.0,“min”:1.0,“max”:1.0}}} 异常检测结果> {“abnormaltype”:1,“paracode”:“EP000001”,“datavalue”:1.0,“tag”:“DA-DB195-RH-B-0201”,“triggertime”:“2025-08-06 09:03”,“statusflag”:1} 报告异常: 类型=1, 状态=异常开始, 标签=DA-DB195-RH-B-0201, 编码=EP000001, 时间=2025-08-06 09:03 同步检测警告: 关联值未初始化 [主标签=DA-DB195-RH-B-0201(EP000001), 关联标签=DA-LT-4BT0007(EP000022)] 加载配置: 30 个参数 配置加载完成,检查点时间: Wed Aug 06 09:04:31 CST 2025 广播配置更新完成, 配置项: {EP000015=ULEParamConfig(tag=DA-LT-6BT005, encode=EP000015, datatype=436887289021410181, constantvalue=1, iszero=1, isonline=1, issync=0, syncparaencode=, ishigh=1, highthreshold=10000.0, islow=1, lowthreshold=10.0, duration=3), EP000014=ULEParamConfig(tag=DA_DB195_RH_R_0281, encode=EP000014, datatype=436887289021410181, constantvalue=1, iszero=1, isonline=1, issync=0, syncparaencode=, ishigh=1, highthreshold=10000.0, islow=1, lowthreshold=10.0, duration=3), EP000013=ULEParamConfig(tag=DA-LT-6BT008, encode=EP000013, datatype=436887289021410181, constantvalue=1, iszero=1, isonline=1, issync=0, syncparaencode=, ishigh=1, highthreshold=10000.0, islow=1, lowthreshold=10.0, duration=3), EP000012=ULEParamConfig(tag=DA-LT-6BT004, encode=EP000012, datatype=436887289021410181, constantvalue=1, iszero=1, isonline=1, issync=0, syncparaencode=, ishigh=1, highthreshold=10000.0, islow=1, lowthreshold=10.0, duration=3), EP000011=ULEParamConfig(tag=DA-LT-6BT005, encode=EP000011, datatype=436887289021410181, constantvalue=1, iszero=1, isonline=1, issync=0, syncparaencode=, ishigh=1, highthreshold=10000.0, islow=1, lowthreshold=10.0, duration=3), EP000010=ULEParamConfig(tag=DA-LT-6BT001, encode=EP000010, datatype=436887289021410181, constantvalue=1, iszero=1, isonline=1, issync=0, syncparaencode=, ishigh=1, highthreshold=10000.0, islow=1, lowthreshold=10.0, duration=3), EP100013=ULEParamConfig(tag=DA-LT-6BT008, encode=EP100013, datatype=436887289021410181, constantvalue=1, iszero=1, isonline=1, issync=0, syncparaencode=, ishigh=1, highthreshold=10000.0, islow=1, lowthreshold=10.0, duration=3), EP100014=ULEParamConfig(tag=DA_DB195_RH_R_0281, encode=EP100014, datatype=436887289021410181, constantvalue=1, iszero=1, isonline=1, issync=0, syncparaencode=, ishigh=1, highthreshold=10000.0, islow=1, lowthreshold=10.0, duration=3), EP000019=ULEParamConfig(tag=DA-LT-5BT0005, encode=EP000019, datatype=436887248101780357, constantvalue=1, iszero=1, isonline=1, issync=0, syncparaencode=, ishigh=1, highthreshold=10000.0, islow=1, lowthreshold=10.0, duration=3), EP000018=ULEParamConfig(tag=DA-LT-4BT0004, encode=EP000018, datatype=436887289021410181, constantvalue=1, iszero=1, isonline=1, issync=0, syncparaencode=, ishigh=1, highthreshold=10000.0, islow=1, lowthreshold=10.0, duration=3), EP100010=ULEParamConfig(tag=DA-LT-6BT001, encode=EP100010, datatype=436887289021410181, constantvalue=1, iszero=1, isonline=1, issync=0, syncparaencode=, ishigh=1, highthreshold=10000.0, islow=1, lowthreshold=10.0, duration=3), EP000017=ULEParamConfig(tag=DA-LT-4BT0005, encode=EP000017, datatype=436887289021410181, constantvalue=1, iszero=1, isonline=1, issync=0, syncparaencode=, ishigh=1, highthreshold=10000.0, islow=1, lowthreshold=10.0, duration=3), EP100011=ULEParamConfig(tag=DA-LT-6BT005, encode=EP100011, datatype=436887289021410181, constantvalue=1, iszero=1, isonline=1, issync=0, syncparaencode=, ishigh=1, highthreshold=10000.0, islow=1, lowthreshold=10.0, duration=3), EP000016=ULEParamConfig(tag=DA-LT-6BT004, encode=EP000016, datatype=436887289021410181, constantvalue=1, iszero=1, isonline=1, issync=0, syncparaencode=, ishigh=1, highthreshold=10000.0, islow=1, lowthreshold=10.0, duration=3), EP100012=ULEParamConfig(tag=DA-LT-6BT004, encode=EP100012, datatype=436887289021410181, constantvalue=1, iszero=1, isonline=1, issync=0, syncparaencode=, ishigh=1, highthreshold=10000.0, islow=1, lowthreshold=10.0, duration=3), EP000004=ULEParamConfig(tag=DA-LT-4BT0004, encode=EP000004, datatype=436887289021410181, constantvalue=1, iszero=1, isonline=1, issync=0, syncparaencode=, ishigh=1, highthreshold=10000.0, islow=1, lowthreshold=10.0, duration=3), EP000003=ULEParamConfig(tag=DA-LT-4BT0008, encode=EP000003, datatype=436887289021410181, constantvalue=1, iszero=1, isonline=1, issync=0, syncparaencode=, ishigh=1, highthreshold=10000.0, islow=1, lowthreshold=10.0, duration=3), EP000002=ULEParamConfig(tag=DA-LT-4BT0001, encode=EP000002, datatype=436887289021410181, constantvalue=1, iszero=1, isonline=1, issync=0, syncparaencode=, ishigh=1, highthreshold=10000.0, islow=1, lowthreshold=10.0, duration=3), EP000001=ULEParamConfig(tag=DA-DB195-RH-B-0201, encode=EP000001, datatype=436887485805570949, constantvalue=1, iszero=1, isonline=1, issync=1, syncparaencode=EP000022, ishigh=1, highthreshold=10000.0, islow=1, lowthreshold=10.0, duration=3), EP000022=ULEParamConfig(tag=DA-LT-4BT0007, encode=EP000022, datatype=436887289021410181, constantvalue=1, iszero=1, isonline=1, issync=0, syncparaencode=, ishigh=1, highthreshold=10000.0, islow=1, lowthreshold=10.0, duration=3), EP000021=ULEParamConfig(tag=DA-LT-6BT008, encode=EP000021, datatype=436887289021410181, constantvalue=1, iszero=1, isonline=1, issync=0, syncparaencode=, ishigh=1, highthreshold=10000.0, islow=1, lowthreshold=10.0, duration=3), EP100007=ULEParamConfig(tag=DA-LT-5BT0005, encode=EP100007, datatype=436887289021410181, constantvalue=1, iszero=1, isonline=1, issync=0, syncparaencode=, ishigh=1, highthreshold=10000.0, islow=1, lowthreshold=10.0, duration=3), EP000020=ULEParamConfig(tag=DA-LT-6BT005, encode=EP000020, datatype=436887248101780357, constantvalue=1, iszero=1, isonline=1, issync=0, syncparaencode=, ishigh=1, highthreshold=10000.0, islow=1, lowthreshold=10.0, duration=3), EP100008=ULEParamConfig(tag=DA-LT-5BT0005, encode=EP100008, datatype=436887289021410181, constantvalue=1, iszero=1, isonline=1, issync=0, syncparaencode=, ishigh=1, highthreshold=10000.0, islow=1, lowthreshold=10.0, duration=3), EP100009=ULEParamConfig(tag=DA-LT-5BT0008, encode=EP100009, datatype=436887289021410181, constantvalue=1, iszero=1, isonline=1, issync=0, syncparaencode=, ishigh=1, highthreshold=10000.0, islow=1, lowthreshold=10.0, duration=3), EP000009=ULEParamConfig(tag=DA-LT-5BT0008, encode=EP000009, datatype=436887289021410181, constantvalue=1, iszero=1, isonline=1, issync=0, syncparaencode=, ishigh=1, highthreshold=10000.0, islow=1, lowthreshold=10.0, duration=3), EP000008=ULEParamConfig(tag=DA-LT-5BT0004, encode=EP000008, datatype=436887289021410181, constantvalue=1, iszero=1, isonline=1, issync=0, syncparaencode=, ishigh=1, highthreshold=10000.0, islow=1, lowthreshold=10.0, duration=3), EP000007=ULEParamConfig(tag=DA-LT-5BT0004, encode=EP000007, datatype=436887289021410181, constantvalue=1, iszero=1, isonline=1, issync=0, syncparaencode=, ishigh=1, highthreshold=10000.0, islow=1, lowthreshold=10.0, duration=3), EP000006=ULEParamConfig(tag=DA-LT-5BT0001, encode=EP000006, datatype=436887289021410181, constantvalue=1, iszero=1, isonline=1, issync=0, syncparaencode=, ishigh=1, highthreshold=10000.0, islow=1, lowthreshold=10.0, duration=3), EP000005=ULEParamConfig(tag=DA-LT-4BT0005, encode=EP000005, datatype=436887289021410181, constantvalue=1, iszero=1, isonline=1, issync=0, syncparaencode=, ishigh=1, highthreshold=10000.0, islow=1, lowthreshold=10.0, duration=3)} 分钟数据流> {“times”:“2025-08-06 09:04”,“datas”:{“DA-LT-5BT0001”:{“ontime”:3100.3306,“avg”:3065.7876,“min”:3011.8132,“max”:3125.8328},“DA-LT-6BT008”:{“ontime”:184.0428,“avg”:183.2953,“min”:181.8443,“max”:184.0821},“DA-LT-5BT0005”:{“ontime”:401.4,“avg”:402.458,“min”:401.4,“max”:403.62},“DA-LT-6BT004”:{“ontime”:1211.7339,“avg”:1211.5092,“min”:1211.3668,“max”:1211.7673},“DA-LT-5BT0004”:{“ontime”:1207.1,“avg”:1208.885,“min”:1207.1,“max”:1210.6},“DA-LT-6BT005”:{“ontime”:402.7474,“avg”:401.5958,“min”:400.9022,“max”:402.7474},“DA-LT-5BT0008”:{“ontime”:184.36,“avg”:184.4267,“min”:183.92,“max”:186.34},“DA-NY-LG1ZL-2-001”:{“ontime”:0.0,“avg”:0.0,“min”:0.0,“max”:0.0},“DA-LT-4BT0008”:{“ontime”:148.8875,“avg”:145.5819,“min”:144.6125,“max”:148.8875},“DB5701P250A00_101”:{“ontime”:1.0,“avg”:1.0,“min”:1.0,“max”:1.0},“DA-LT-4BT0004”:{“ontime”:1209.0,“avg”:1209.0,“min”:1209.0,“max”:1209.0},“DA-LT-6BT001”:{“ontime”:178735.3,“avg”:178495.8288,“min”:177315.8,“max”:179603.86},“DA-NY-LG2ZL-2-003”:{“ontime”:1.0,“avg”:1.0,“min”:1.0,“max”:1.0},“DA-LT-4BT0005”:{“ontime”:318.1875,“avg”:314.7302,“min”:313.9375,“max”:318.1875},“DA_DB195_RH_R_0281”:{“ontime”:345.1,“avg”:344.8548,“min”:305.45,“max”:374.1},“DA-DB195-RH-B-0200”:{“ontime”:1.0,“avg”:1.0,“min”:1.0,“max”:1.0},“DA-DB195-RH-B-0201”:{“ontime”:1.0,“avg”:1.0,“min”:1.0,“max”:1.0},“DA-LT-4BT0001”:{“ontime”:116909.22,“avg”:116579.9132,“min”:115389.78,“max”:117707.77}}} 同步检测警告: 关联值未初始化 [主标签=DA-DB195-RH-B-0201(EP000001), 关联标签=DA-LT-4BT0007(EP000022)]。需完善内容,1、所有异常发生时间应为实际异常出现的开始时间,不应为加duration后的时间;2、DA-LT-4BT0007离线监测未实现。
最新发布
08-07
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值