(转) Don't Use System.out.println! Use Log4j

本文介绍如何使用Log4j进行日志记录,包括配置文件设置、不同类型的Appender及Layout选项,并展示了如何根据不同需求调整日志级别。

原文地址: Don't Use System.out.println! Use Log4j


Home: www.vipan.com Vipan Singla e-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.

  • 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.
  • %n : Outputs the platform-dependent newline character(s). Preferable to specifying "\n" or "\r\n" etc.
  • %% : 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. 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} , %d{ABSOLUTE}, %d{RELATIVE} (millisecs since program start, fastest ) or %d{DATE} which use log4j's ISO8601DateFormat, AbsoluteTimeDateFormat, RelativeTimeDateFormat and DateTimeDateFormat date formatters respectively.
  • %l : Outputs source code location information. Shortcut for %C.%M(%F:%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! log4j.disableOverride=false : Default is false . Sometimes set to true to be able to ignore log.disable property.

ConsoleAppender Options

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. 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

PatternLayout Options

ConversionPattern=%m%n : How to format each log message (What information to include).

HTMLLayout Options

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
private void performTraversals() { 3295 mLastPerformTraversalsSkipDrawReason = null; 3296 3297 // cache mView since it is used so much below... 3298 final View host = mView; 3299 if (DBG) { 3300 System.out.println("======================================"); 3301 System.out.println("performTraversals"); 3302 host.debug(); 3303 } 3304 3305 if (host == null || !mAdded) { 3306 mLastPerformTraversalsSkipDrawReason = host == null ? "no_host" : "not_added"; 3307 return; 3308 } 3309 3310 if (mNumPausedForSync > 0) { 3311 if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) { 3312 Trace.instant(Trace.TRACE_TAG_VIEW, 3313 TextUtils.formatSimple("performTraversals#mNumPausedForSync=%d", 3314 mNumPausedForSync)); 3315 } 3316 3317 Log.d(mTag, "Skipping traversal due to sync " + mNumPausedForSync); 3318 mLastPerformTraversalsSkipDrawReason = "paused_for_sync"; 3319 return; 3320 } 3321 3322 // #ifdef OPLUS_FEATURE_VIEW_DEBUG 3323 // Bard.Zhang@Android.UIFramework 2023-01-28 Add for : view debug 3324 mViewRootImplExt.markOnPerformTraversalsStart(host, mFirst); 3325 // #endif /* OPLUS_FEATURE_VIEW_DEBUG */ 3326 3327 mIsInTraversal = true; 3328 mWillDrawSoon = true; 3329 boolean cancelDraw = false; 3330 String cancelReason = null; 3331 boolean isSyncRequest = false; 3332 3333 boolean windowSizeMayChange = false; 3334 WindowManager.LayoutParams lp = mWindowAttributes; 3335 3336 int desiredWindowWidth; 3337 int desiredWindowHeight; 3338 3339 final int viewVisibility = getHostVisibility(); 3340 final boolean viewVisibilityChanged = !mFirst 3341 && (mViewVisibility != viewVisibility || mNewSurfaceNeeded 3342 // Also check for possible double visibility update, which will make current 3343 // viewVisibility value equal to mViewVisibility and we may miss it. 3344 || mAppVisibilityChanged); 3345 mAppVisibilityChanged = false; 3346 final boolean viewUserVisibilityChanged = !mFirst && 3347 ((mViewVisibility == View.VISIBLE) != (viewVisibility == View.VISIBLE)); 3348 final boolean shouldOptimizeMeasure = shouldOptimizeMeasure(lp); 3349 3350 WindowManager.LayoutParams params = null; 3351 CompatibilityInfo compatibilityInfo = 3352 mDisplay.getDisplayAdjustments().getCompatibilityInfo(); 3353 if (compatibilityInfo.supportsScreen() == mLastInCompatMode) { 3354 params = lp; 3355 mFullRedrawNeeded = true; 3356 mLayoutRequested = true; 3357 if (mLastInCompatMode) { 3358 params.privateFlags &= ~WindowManager.LayoutParams.PRIVATE_FLAG_COMPATIBLE_WINDOW; 3359 mLastInCompatMode = false; 3360 } else { 3361 params.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_COMPATIBLE_WINDOW; 3362 mLastInCompatMode = true; 3363 } 3364 } 3365 3366 Rect frame = mWinFrame; 3367 if (mFirst) { 3368 mFullRedrawNeeded = true; 3369 mLayoutRequested = true; 3370 3371 final Configuration config = getConfiguration(); 3372 if (shouldUseDisplaySize(lp)) { 3373 // NOTE -- system code, won't try to do compat mode. 3374 Point size = new Point(); 3375 mDisplay.getRealSize(size); 3376 desiredWindowWidth = size.x; 3377 desiredWindowHeight = size.y; 3378 } else if (lp.width == ViewGroup.LayoutParams.WRAP_CONTENT 3379 || lp.height == ViewGroup.LayoutParams.WRAP_CONTENT) { 3380 // For wrap content, we have to remeasure later on anyways. Use size consistent with 3381 // below so we get best use of the measure cache. 3382 final Rect bounds = getWindowBoundsInsetSystemBars(); 3383 desiredWindowWidth = bounds.width(); 3384 desiredWindowHeight = bounds.height(); 3385 } else { 3386 // After addToDisplay, the frame contains the frameHint from window manager, which 3387 // for most windows is going to be the same size as the result of relayoutWindow. 3388 // Using this here allows us to avoid remeasuring after relayoutWindow 3389 desiredWindowWidth = frame.width(); 3390 desiredWindowHeight = frame.height(); 3391 } 3392 3393 // We used to use the following condition to choose 32 bits drawing caches: 3394 // PixelFormat.hasAlpha(lp.format) || lp.format == PixelFormat.RGBX_8888 3395 // However, windows are now always 32 bits by default, so choose 32 bits 3396 mAttachInfo.mUse32BitDrawingCache = true; 3397 mAttachInfo.mWindowVisibility = viewVisibility; 3398 mAttachInfo.mRecomputeGlobalAttributes = false; 3399 mLastConfigurationFromResources.setTo(config); 3400 mLastSystemUiVisibility = mAttachInfo.mSystemUiVisibility; 3401 // Set the layout direction if it has not been set before (inherit is the default) 3402 if (mViewLayoutDirectionInitial == View.LAYOUT_DIRECTION_INHERIT) { 3403 host.setLayoutDirection(config.getLayoutDirection()); 3404 } 3405 //#ifdef OPLUS_FEATURE_BRACKETMODE_2_0 3406 //wenguangyu@ANDROID.WMS, 2022/10/02, add for bracket mode 3407 mViewRootImplExt.attachToWindow(); 3408 //#endif /*OPLUS_FEATURE_BRACKETMODE_2_0*/ 3409 host.dispatchAttachedToWindow(mAttachInfo, 0); 3410 mAttachInfo.mTreeObserver.dispatchOnWindowAttachedChange(true); 3411 dispatchApplyInsets(host); 3412 if (!mOnBackInvokedDispatcher.isOnBackInvokedCallbackEnabled()) { 3413 // For apps requesting legacy back behavior, we add a compat callback that 3414 // dispatches {@link KeyEvent#KEYCODE_BACK} to their root views. 3415 // This way from system point of view, these apps are providing custom 3416 // {@link OnBackInvokedCallback}s, and will not play system back animations 3417 // for them. 3418 registerCompatOnBackInvokedCallback(); 3419 } 3420 } else { 3421 desiredWindowWidth = frame.width(); 3422 desiredWindowHeight = frame.height(); 3423 if (desiredWindowWidth != mWidth || desiredWindowHeight != mHeight) { 3424 if (DEBUG_ORIENTATION) Log.v(mTag, "View " + host + " resized to: " + frame); 3425 mFullRedrawNeeded = true; 3426 mLayoutRequested = true; 3427 windowSizeMayChange = true; 3428 } 3429 } 3430 3431 if (viewVisibilityChanged) { 3432 mAttachInfo.mWindowVisibility = viewVisibility; 3433 host.dispatchWindowVisibilityChanged(viewVisibility); 3434 mAttachInfo.mTreeObserver.dispatchOnWindowVisibilityChange(viewVisibility); 3435 if (viewUserVisibilityChanged) { 3436 host.dispatchVisibilityAggregated(viewVisibility == View.VISIBLE); 3437 } 3438 if (viewVisibility != View.VISIBLE || mNewSurfaceNeeded) { 3439 endDragResizing(); 3440 destroyHardwareResources(); 3441 } 3442 } 3443 3444 // Non-visible windows can't hold accessibility focus. 3445 if (mAttachInfo.mWindowVisibility != View.VISIBLE) { 3446 host.clearAccessibilityFocus(); 3447 } 3448 3449 // Execute enqueued actions on every traversal in case a detached view enqueued an action 3450 getRunQueue().executeActions(mAttachInfo.mHandler); 3451 3452 if (mFirst) { 3453 // make sure touch mode code executes by setting cached value 3454 // to opposite of the added touch mode. 3455 mAttachInfo.mInTouchMode = !mAddedTouchMode; 3456 ensureTouchModeLocally(mAddedTouchMode); 3457 } 3458 3459 boolean layoutRequested = mLayoutRequested && (!mStopped || mReportNextDraw); 3460 if (layoutRequested) { 3461 if (!mFirst) { 3462 if (lp.width == ViewGroup.LayoutParams.WRAP_CONTENT 3463 || lp.height == ViewGroup.LayoutParams.WRAP_CONTENT) { 3464 windowSizeMayChange = true; 3465 3466 if (shouldUseDisplaySize(lp)) { 3467 // NOTE -- system code, won't try to do compat mode. 3468 Point size = new Point(); 3469 mDisplay.getRealSize(size); 3470 desiredWindowWidth = size.x; 3471 desiredWindowHeight = size.y; 3472 } else { 3473 final Rect bounds = getWindowBoundsInsetSystemBars(); 3474 desiredWindowWidth = bounds.width(); 3475 desiredWindowHeight = bounds.height(); 3476 } 3477 } 3478 } 3479 3480 // Ask host how big it wants to be 3481 windowSizeMayChange |= measureHierarchy(host, lp, mView.getContext().getResources(), 3482 desiredWindowWidth, desiredWindowHeight, shouldOptimizeMeasure); 3483 } 3484 3485 if (collectViewAttributes()) { 3486 params = lp; 3487 } 3488 if (mAttachInfo.mForceReportNewAttributes) { 3489 mAttachInfo.mForceReportNewAttributes = false; 3490 params = lp; 3491 } 3492 3493 if (mFirst || mAttachInfo.mViewVisibilityChanged) { 3494 mAttachInfo.mViewVisibilityChanged = false; 3495 int resizeMode = mSoftInputMode & SOFT_INPUT_MASK_ADJUST; 3496 // If we are in auto resize mode, then we need to determine 3497 // what mode to use now. 3498 if (resizeMode == WindowManager.LayoutParams.SOFT_INPUT_ADJUST_UNSPECIFIED) { 3499 final int N = mAttachInfo.mScrollContainers.size(); 3500 for (int i=0; i<N; i++) { 3501 if (mAttachInfo.mScrollContainers.get(i).isShown()) { 3502 resizeMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE; 3503 } 3504 } 3505 if (resizeMode == 0) { 3506 resizeMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN; 3507 } 3508 if ((lp.softInputMode & SOFT_INPUT_MASK_ADJUST) != resizeMode) { 3509 lp.softInputMode = (lp.softInputMode & ~SOFT_INPUT_MASK_ADJUST) | resizeMode; 3510 params = lp; 3511 } 3512 } 3513 } 3514 3515 if (mApplyInsetsRequested) { 3516 dispatchApplyInsets(host); 3517 if (mLayoutRequested) { 3518 // Short-circuit catching a new layout request here, so 3519 // we don't need to go through two layout passes when things 3520 // change due to fitting system windows, which can happen a lot. 3521 windowSizeMayChange |= measureHierarchy(host, lp, 3522 mView.getContext().getResources(), desiredWindowWidth, desiredWindowHeight, 3523 shouldOptimizeMeasure); 3524 } 3525 } 3526 3527 if (layoutRequested) { 3528 // Clear this now, so that if anything requests a layout in the 3529 // rest of this function we will catch it and re-run a full 3530 // layout pass. 3531 mLayoutRequested = false; 3532 } 3533 3534 boolean windowShouldResize = layoutRequested && windowSizeMayChange 3535 && ((mWidth != host.getMeasuredWidth() || mHeight != host.getMeasuredHeight()) 3536 || (lp.width == ViewGroup.LayoutParams.WRAP_CONTENT && 3537 frame.width() < desiredWindowWidth && frame.width() != mWidth) 3538 || (lp.height == ViewGroup.LayoutParams.WRAP_CONTENT && 3539 frame.height() < desiredWindowHeight && frame.height() != mHeight)); 3540 windowShouldResize |= mDragResizing && mPendingDragResizing; 3541 3542 // Determine whether to compute insets. 3543 // If there are no inset listeners remaining then we may still need to compute 3544 // insets in case the old insets were non-empty and must be reset. 3545 final boolean computesInternalInsets = 3546 mAttachInfo.mTreeObserver.hasComputeInternalInsetsListeners() 3547 || mAttachInfo.mHasNonEmptyGivenInternalInsets; 3548 3549 boolean insetsPending = false; 3550 int relayoutResult = 0; 3551 boolean updatedConfiguration = false; 3552 3553 final int surfaceGenerationId = mSurface.getGenerationId(); 3554 3555 final boolean isViewVisible = viewVisibility == View.VISIBLE; 3556 boolean surfaceSizeChanged = false; 3557 boolean surfaceCreated = false; 3558 boolean surfaceDestroyed = false; 3559 // True if surface generation id changes or relayout result is RELAYOUT_RES_SURFACE_CHANGED. 3560 boolean surfaceReplaced = false; 3561 3562 final boolean windowAttributesChanged = mWindowAttributesChanged; 3563 if (windowAttributesChanged) { 3564 mWindowAttributesChanged = false; 3565 params = lp; 3566 } 3567 3568 if (params != null) { 3569 if ((host.mPrivateFlags & View.PFLAG_REQUEST_TRANSPARENT_REGIONS) != 0 3570 && !PixelFormat.formatHasAlpha(params.format)) { 3571 params.format = PixelFormat.TRANSLUCENT; 3572 } 3573 adjustLayoutParamsForCompatibility(params); 3574 controlInsetsForCompatibility(params); 3575 if (mDispatchedSystemBarAppearance != params.insetsFlags.appearance) { 3576 mDispatchedSystemBarAppearance = params.insetsFlags.appearance; 3577 mView.onSystemBarAppearanceChanged(mDispatchedSystemBarAppearance); 3578 } 3579 } 3580 3581 if (mFirst || windowShouldResize || viewVisibilityChanged || params != null 3582 || mForceNextWindowRelayout) { 3583 if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) { 3584 Trace.traceBegin(Trace.TRACE_TAG_VIEW, 3585 TextUtils.formatSimple("relayoutWindow#" 3586 + "first=%b/resize=%b/vis=%b/params=%b/force=%b", 3587 mFirst, windowShouldResize, viewVisibilityChanged, params != null, 3588 mForceNextWindowRelayout)); 3589 } 3590 3591 mForceNextWindowRelayout = false; 3592 3593 // If this window is giving internal insets to the window manager, then we want to first 3594 // make the provided insets unchanged during layout. This avoids it briefly causing 3595 // other windows to resize/move based on the raw frame of the window, waiting until we 3596 // can finish laying out this window and get back to the window manager with the 3597 // ultimately computed insets. 3598 insetsPending = computesInternalInsets; 3599 3600 if (mSurfaceHolder != null) { 3601 mSurfaceHolder.mSurfaceLock.lock(); 3602 mDrawingAllowed = true; 3603 } 3604 3605 boolean hwInitialized = false; 3606 boolean dispatchApplyInsets = false; 3607 boolean hadSurface = mSurface.isValid(); 3608 3609 try { 3610 if (DEBUG_LAYOUT) { 3611 Log.i(mTag, "host=w:" + host.getMeasuredWidth() + ", h:" + 3612 host.getMeasuredHeight() + ", params=" + params); 3613 } 3614 3615 if (mFirst || viewVisibilityChanged) { 3616 mViewFrameInfo.flags |= FrameInfo.FLAG_WINDOW_VISIBILITY_CHANGED; 3617 } 3618 //#ifdef OPLUS_FEATURE_JANK_TRACKER 3619 //wangwei11@oppo.com, 2021/11/28, Add for janktracker 3620 mChoreographer.mChoreographerExt.markRelayout(); 3621 //#endif /*OPLUS_FEATURE_JANK_TRACKER*/ 3622 relayoutResult = relayoutWindow(params, viewVisibility, insetsPending); 3623 cancelDraw = (relayoutResult & RELAYOUT_RES_CANCEL_AND_REDRAW) 3624 == RELAYOUT_RES_CANCEL_AND_REDRAW; 3625 cancelReason = "relayout"; 3626 final boolean dragResizing = mPendingDragResizing; 3627 if (mSyncSeqId > mLastSyncSeqId) { 3628 mLastSyncSeqId = mSyncSeqId; 3629 if (DEBUG_BLAST) { 3630 Log.d(mTag, "Relayout called with blastSync"); 3631 } 3632 reportNextDraw("relayout"); 3633 mSyncBuffer = true; 3634 isSyncRequest = true; 3635 if (!cancelDraw) { 3636 mDrewOnceForSync = false; 3637 } 3638 } 3639 3640 final boolean surfaceControlChanged = 3641 (relayoutResult & RELAYOUT_RES_SURFACE_CHANGED) 3642 == RELAYOUT_RES_SURFACE_CHANGED; 3643 3644 if (mSurfaceControl.isValid()) { 3645 updateOpacity(mWindowAttributes, dragResizing, 3646 surfaceControlChanged /*forceUpdate */); 3647 // No need to updateDisplayDecoration if it's a new SurfaceControl and 3648 // mDisplayDecorationCached is false, since that's the default for a new 3649 // SurfaceControl. 3650 if (surfaceControlChanged && mDisplayDecorationCached) { 3651 updateDisplayDecoration(); 3652 } 3653 if (surfaceControlChanged 3654 && mWindowAttributes.type 3655 == WindowManager.LayoutParams.TYPE_STATUS_BAR) { 3656 mTransaction.setDefaultFrameRateCompatibility(mSurfaceControl, 3657 Surface.FRAME_RATE_COMPATIBILITY_NO_VOTE).apply(); 3658 } 3659 } 3660 3661 if (DEBUG_LAYOUT) Log.v(mTag, "relayout: frame=" + frame.toShortString() 3662 + " surface=" + mSurface); 3663 3664 // If the pending {@link MergedConfiguration} handed back from 3665 // {@link #relayoutWindow} does not match the one last reported, 3666 // WindowManagerService has reported back a frame from a configuration not yet 3667 // handled by the client. In this case, we need to accept the configuration so we 3668 // do not lay out and draw with the wrong configuration. 3669 if (mRelayoutRequested 3670 && !mPendingMergedConfiguration.equals(mLastReportedMergedConfiguration)) { 3671 if (DEBUG_CONFIGURATION) Log.v(mTag, "Visible with new config: " 3672 + mPendingMergedConfiguration.getMergedConfiguration()); 3673 performConfigurationChange(new MergedConfiguration(mPendingMergedConfiguration), 3674 !mFirst, INVALID_DISPLAY /* same display */); 3675 updatedConfiguration = true; 3676 } 3677 final boolean updateSurfaceNeeded = mUpdateSurfaceNeeded; 3678 mUpdateSurfaceNeeded = false; 3679 3680 surfaceSizeChanged = false; 3681 if (!mLastSurfaceSize.equals(mSurfaceSize)) { 3682 surfaceSizeChanged = true; 3683 mLastSurfaceSize.set(mSurfaceSize.x, mSurfaceSize.y); 3684 } 3685 final boolean alwaysConsumeSystemBarsChanged = 3686 mPendingAlwaysConsumeSystemBars != mAttachInfo.mAlwaysConsumeSystemBars; 3687 updateColorModeIfNeeded(lp.getColorMode()); 3688 surfaceCreated = !hadSurface && mSurface.isValid(); 3689 surfaceDestroyed = hadSurface && !mSurface.isValid(); 3690 3691 // When using Blast, the surface generation id may not change when there's a new 3692 // SurfaceControl. In that case, we also check relayout flag 3693 // RELAYOUT_RES_SURFACE_CHANGED since it should indicate that WMS created a new 3694 // SurfaceControl. 3695 surfaceReplaced = (surfaceGenerationId != mSurface.getGenerationId() 3696 || surfaceControlChanged) && mSurface.isValid(); 3697 if (surfaceReplaced) { 3698 mSurfaceSequenceId++; 3699 } 3700 if (alwaysConsumeSystemBarsChanged) { 3701 mAttachInfo.mAlwaysConsumeSystemBars = mPendingAlwaysConsumeSystemBars; 3702 dispatchApplyInsets = true; 3703 } 3704 if (updateCaptionInsets()) { 3705 dispatchApplyInsets = true; 3706 } 3707 if (dispatchApplyInsets || mLastSystemUiVisibility != 3708 mAttachInfo.mSystemUiVisibility || mApplyInsetsRequested) { 3709 mLastSystemUiVisibility = mAttachInfo.mSystemUiVisibility; 3710 dispatchApplyInsets(host); 3711 // We applied insets so force contentInsetsChanged to ensure the 3712 // hierarchy is measured below. 3713 dispatchApplyInsets = true; 3714 } 3715 3716 if (surfaceCreated) { 3717 // If we are creating a new surface, then we need to 3718 // completely redraw it. 3719 mFullRedrawNeeded = true; 3720 mPreviousTransparentRegion.setEmpty(); 3721 3722 // Only initialize up-front if transparent regions are not 3723 // requested, otherwise defer to see if the entire window 3724 // will be transparent 3725 if (mAttachInfo.mThreadedRenderer != null) { 3726 try { 3727 hwInitialized = mAttachInfo.mThreadedRenderer.initialize(mSurface); 3728 // #ifdef OPLUS_EXTENSION_HOOK 3729 // yanliang@Android.Performance 2023-02-13 Add for Optimize sliding effect 3730 mViewRootImplExt.setPendingBufferCountSetting(true); 3731 // #endif /*OPLUS_EXTENSION_HOOK*/ 3732 if (hwInitialized && (host.mPrivateFlags 3733 //#ifndef OPLUS_BUG_STABILITY 3734 //Weitao.Chen@ANDROID.STABILITY.2742412, 2020/01/02, Add for null pointer exp 3735 //& View.PFLAG_REQUEST_TRANSPARENT_REGIONS) == 0) { 3736 //#else 3737 & View.PFLAG_REQUEST_TRANSPARENT_REGIONS) == 0 && mAttachInfo.mThreadedRenderer != null) { 3738 //#endif /*OPLUS_BUG_STABILITY*/ 3739 // Don't pre-allocate if transparent regions 3740 // are requested as they may not be needed 3741 mAttachInfo.mThreadedRenderer.allocateBuffers(); 3742 } 3743 } catch (OutOfResourcesException e) { 3744 handleOutOfResourcesException(e); 3745 mLastPerformTraversalsSkipDrawReason = "oom_initialize_renderer"; 3746 return; 3747 } 3748 } 3749 } else if (surfaceDestroyed) { 3750 // If the surface has been removed, then reset the scroll 3751 // positions. 3752 if (mLastScrolledFocus != null) { 3753 mLastScrolledFocus.clear(); 3754 } 3755 mScrollY = mCurScrollY = 0; 3756 if (mView instanceof RootViewSurfaceTaker) { 3757 ((RootViewSurfaceTaker) mView).onRootViewScrollYChanged(mCurScrollY); 3758 } 3759 if (mScroller != null) { 3760 mScroller.abortAnimation(); 3761 } 3762 // Our surface is gone 3763 if (isHardwareEnabled()) { 3764 mAttachInfo.mThreadedRenderer.destroy(); 3765 } 3766 } else if ((surfaceReplaced || surfaceSizeChanged || updateSurfaceNeeded) 3767 && mSurfaceHolder == null 3768 && mAttachInfo.mThreadedRenderer != null 3769 && mSurface.isValid()) { 3770 mFullRedrawNeeded = true; 3771 try { 3772 // Need to do updateSurface (which leads to CanvasContext::setSurface and 3773 // re-create the EGLSurface) if either the Surface changed (as indicated by 3774 // generation id), or WindowManager changed the surface size. The latter is 3775 // because on some chips, changing the consumer side's BufferQueue size may 3776 // not take effect immediately unless we create a new EGLSurface. 3777 // Note that frame size change doesn't always imply surface size change (eg. 3778 // drag resizing uses fullscreen surface), need to check surfaceSizeChanged 3779 // flag from WindowManager. 3780 mAttachInfo.mThreadedRenderer.updateSurface(mSurface); 3781 } catch (OutOfResourcesException e) { 3782 handleOutOfResourcesException(e); 3783 mLastPerformTraversalsSkipDrawReason = "oom_update_surface"; 3784 return; 3785 } 3786 } 3787 3788 if (mDragResizing != dragResizing) { 3789 if (dragResizing) { 3790 final boolean backdropSizeMatchesFrame = 3791 mWinFrame.width() == mPendingBackDropFrame.width() 3792 && mWinFrame.height() == mPendingBackDropFrame.height(); 3793 // TODO: Need cutout? 3794 startDragResizing(mPendingBackDropFrame, !backdropSizeMatchesFrame, 3795 mAttachInfo.mContentInsets, mAttachInfo.mStableInsets); 3796 } else { 3797 // We shouldn't come here, but if we come we should end the resize. 3798 endDragResizing(); 3799 } 3800 } 3801 if (!mUseMTRenderer) { 3802 if (dragResizing) { 3803 mCanvasOffsetX = mWinFrame.left; 3804 mCanvasOffsetY = mWinFrame.top; 3805 } else { 3806 mCanvasOffsetX = mCanvasOffsetY = 0; 3807 } 3808 } 3809 } catch (RemoteException e) { 3810 } finally { 3811 if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) { 3812 Trace.traceEnd(Trace.TRACE_TAG_VIEW); 3813 } 3814 } 3815 3816 if (DEBUG_ORIENTATION) Log.v( 3817 TAG, "Relayout returned: frame=" + frame + ", surface=" + mSurface); 3818 3819 mAttachInfo.mWindowLeft = frame.left; 3820 mAttachInfo.mWindowTop = frame.top; 3821 3822 // !!FIXME!! This next section handles the case where we did not get the 3823 // window size we asked for. We should avoid this by getting a maximum size from 3824 // the window session beforehand. 3825 if (mWidth != frame.width() || mHeight != frame.height()) { 3826 mWidth = frame.width(); 3827 mHeight = frame.height(); 3828 } 3829 3830 if (mSurfaceHolder != null) { 3831 // The app owns the surface; tell it about what is going on. 3832 if (mSurface.isValid()) { 3833 // XXX .copyFrom() doesn't work! 3834 //mSurfaceHolder.mSurface.copyFrom(mSurface); 3835 mSurfaceHolder.mSurface = mSurface; 3836 } 3837 mSurfaceHolder.setSurfaceFrameSize(mWidth, mHeight); 3838 mSurfaceHolder.mSurfaceLock.unlock(); 3839 if (surfaceCreated) { 3840 mSurfaceHolder.ungetCallbacks(); 3841 3842 mIsCreating = true; 3843 SurfaceHolder.Callback[] callbacks = mSurfaceHolder.getCallbacks(); 3844 if (callbacks != null) { 3845 for (SurfaceHolder.Callback c : callbacks) { 3846 c.surfaceCreated(mSurfaceHolder); 3847 } 3848 } 3849 } 3850 3851 if ((surfaceCreated || surfaceReplaced || surfaceSizeChanged 3852 || windowAttributesChanged) && mSurface.isValid()) { 3853 SurfaceHolder.Callback[] callbacks = mSurfaceHolder.getCallbacks(); 3854 if (callbacks != null) { 3855 for (SurfaceHolder.Callback c : callbacks) { 3856 c.surfaceChanged(mSurfaceHolder, lp.format, 3857 mWidth, mHeight); 3858 } 3859 } 3860 mIsCreating = false; 3861 } 3862 3863 if (surfaceDestroyed) { 3864 notifyHolderSurfaceDestroyed(); 3865 mSurfaceHolder.mSurfaceLock.lock(); 3866 try { 3867 mSurfaceHolder.mSurface = new Surface(); 3868 } finally { 3869 mSurfaceHolder.mSurfaceLock.unlock(); 3870 } 3871 } 3872 } 3873 3874 final ThreadedRenderer threadedRenderer = mAttachInfo.mThreadedRenderer; 3875 if (threadedRenderer != null && threadedRenderer.isEnabled()) { 3876 if (hwInitialized 3877 || mWidth != threadedRenderer.getWidth() 3878 || mHeight != threadedRenderer.getHeight() 3879 || mNeedsRendererSetup) { 3880 threadedRenderer.setup(mWidth, mHeight, mAttachInfo, 3881 mWindowAttributes.surfaceInsets); 3882 mNeedsRendererSetup = false; 3883 } 3884 } 3885 3886 // TODO: In the CL "ViewRootImpl: Fix issue with early draw report in 3887 // seamless rotation". We moved processing of RELAYOUT_RES_BLAST_SYNC 3888 // earlier in the function, potentially triggering a call to 3889 // reportNextDraw(). That same CL changed this and the next reference 3890 // to wasReportNextDraw, such that this logic would remain undisturbed 3891 // (it continues to operate as if the code was never moved). This was 3892 // done to achieve a more hermetic fix for S, but it's entirely 3893 // possible that checking the most recent value is actually more 3894 // correct here. 3895 if (!mStopped || mReportNextDraw) { 3896 if (mWidth != host.getMeasuredWidth() || mHeight != host.getMeasuredHeight() 3897 || dispatchApplyInsets || updatedConfiguration) { 3898 int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width, 3899 lp.privateFlags); 3900 int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height, 3901 lp.privateFlags); 3902 3903 if (DEBUG_LAYOUT) Log.v(mTag, "Ooops, something changed! mWidth=" 3904 + mWidth + " measuredWidth=" + host.getMeasuredWidth() 3905 + " mHeight=" + mHeight 3906 + " measuredHeight=" + host.getMeasuredHeight() 3907 + " dispatchApplyInsets=" + dispatchApplyInsets); 3908 3909 // #ifdef OPLUS_FEATURE_VIEW_DEBUG 3910 // Bard.Zhang@Android.UIFramework 2023-01-19 Add for : view debug 3911 String measureReason = "Measure reason mFirst " + mFirst 3912 + " windowShouldResize " + windowShouldResize 3913 + " viewVisibilityChanged " + viewVisibilityChanged 3914 + " params != null " + (params != null) 3915 + " mForceNextWindowRelayout " + mForceNextWindowRelayout 3916 + " mStopped " + mStopped + " mReportNextDraw " + mReportNextDraw 3917 + " mWidth " + mWidth + " host.getMeasuredWidth " + host.getMeasuredWidth() 3918 + " mHeight " + mHeight + " host.getMeasuredHeight " + host.getMeasuredHeight() 3919 + " dispatchApplyInsets " + dispatchApplyInsets 3920 + " updateConfiguration " + updatedConfiguration; 3921 mViewRootImplExt.markPerformMeasureReason(measureReason); 3922 // #endif /* OPLUS_FEATURE_VIEW_DEBUG */ 3923 // #ifdef OPLUS_BUG_COMPATIBILITY 3924 //Qian.Jiang@Android.Wms, 2023/05/19: fix bug-5573936 3925 int heightMesure = MeasureSpec.getSize(childHeightMeasureSpec); 3926 int widthMesure = MeasureSpec.getSize(childWidthMeasureSpec); 3927 if (PANIC_DEBUG && (heightMesure > LIMIT || widthMesure > LIMIT)) { 3928 Log.d(mTag, "performMeasure heightMesure = " + heightMesure + ",widthMesure = " + widthMesure); 3929 } 3930 //#endif /* OPLUS_BUG_COMPATIBILITY */ 3931 // Ask host how big it wants to be 3932 performMeasure(childWidthMeasureSpec, childHeightMeasureSpec); 3933 3934 // Implementation of weights from WindowManager.LayoutParams 3935 // We just grow the dimensions as needed and re-measure if 3936 // needs be 3937 int width = host.getMeasuredWidth(); 3938 int height = host.getMeasuredHeight(); 3939 boolean measureAgain = false; 3940 3941 if (lp.horizontalWeight > 0.0f) { 3942 width += (int) ((mWidth - width) * lp.horizontalWeight); 3943 childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(width, 3944 MeasureSpec.EXACTLY); 3945 measureAgain = true; 3946 } 3947 if (lp.verticalWeight > 0.0f) { 3948 height += (int) ((mHeight - height) * lp.verticalWeight); 3949 childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(height, 3950 MeasureSpec.EXACTLY); 3951 measureAgain = true; 3952 } 3953 3954 if (measureAgain) { 3955 if (DEBUG_LAYOUT) Log.v(mTag, 3956 "And hey let's measure once more: width=" + width 3957 + " height=" + height); 3958 3959 // #ifdef OPLUS_FEATURE_VIEW_DEBUG 3960 // Bard.Zhang@Android.UIFramework 2023-01-19 Add for : view debug 3961 mMeasureReason = "3"; 3962 String measureAgainReason = "Measure again lp.horizontalWeight " + lp.horizontalWeight 3963 + " lp.verticalWeight " + lp.verticalWeight; 3964 mViewRootImplExt.markPerformMeasureReason(measureAgainReason); 3965 // #endif /* OPLUS_FEATURE_VIEW_DEBUG */ 3966 3967 // #ifdef OPLUS_BUG_COMPATIBILITY 3968 //Qian.Jiang@Android.Wms, 2023/05/19: fix bug-5573936 3969 int heightAgain = MeasureSpec.getSize(childHeightMeasureSpec); 3970 int widthAgain = MeasureSpec.getSize(childWidthMeasureSpec); 3971 if (PANIC_DEBUG && (heightAgain > LIMIT || widthAgain > LIMIT)) { 3972 Log.d(mTag, "measureAgain performMeasure heightAgain = " + heightAgain + ",widthAgain = " + widthAgain); 3973 } 3974 //#endif /* OPLUS_BUG_COMPATIBILITY */ 3975 performMeasure(childWidthMeasureSpec, childHeightMeasureSpec); 3976 } 3977 3978 layoutRequested = true; 3979 } 3980 } 3981 } else { 3982 // Not the first pass and no window/insets/visibility change but the window 3983 // may have moved and we need check that and if so to update the left and right 3984 // in the attach info. We translate only the window frame since on window move 3985 // the window manager tells us only for the new frame but the insets are the 3986 // same and we do not want to translate them more than once. 3987 maybeHandleWindowMove(frame); 3988 } 3989 3990 if (mViewMeasureDeferred) { 3991 // It's time to measure the views since we are going to layout them. 3992 performMeasure( 3993 MeasureSpec.makeMeasureSpec(frame.width(), MeasureSpec.EXACTLY), 3994 MeasureSpec.makeMeasureSpec(frame.height(), MeasureSpec.EXACTLY)); 3995 } 3996 3997 if (!mRelayoutRequested && mCheckIfCanDraw) { 3998 // We had a sync previously, but we didn't call IWindowSession#relayout in this 3999 // traversal. So we don't know if the sync is complete that we can continue to draw. 4000 // Here invokes cancelDraw to obtain the information. 4001 try { 4002 cancelDraw = mWindowSession.cancelDraw(mWindow); 4003 cancelReason = "wm_sync"; 4004 Log.d(mTag, "cancelDraw returned " + cancelDraw); 4005 } catch (RemoteException e) { 4006 } 4007 } 4008 4009 if (surfaceSizeChanged || surfaceReplaced || surfaceCreated || 4010 windowAttributesChanged || mChildBoundingInsetsChanged) { 4011 // If the surface has been replaced, there's a chance the bounds layer is not parented 4012 // to the new layer. When updating bounds layer, also reparent to the main VRI 4013 // SurfaceControl to ensure it's correctly placed in the hierarchy. 4014 // 4015 // This needs to be done on the client side since WMS won't reparent the children to the 4016 // new surface if it thinks the app is closing. WMS gets the signal that the app is 4017 // stopping, but on the client side it doesn't get stopped since it's restarted quick 4018 // enough. WMS doesn't want to keep around old children since they will leak when the 4019 // client creates new children. 4020 prepareSurfaces(); 4021 mChildBoundingInsetsChanged = false; 4022 } 4023 4024 final boolean didLayout = layoutRequested && (!mStopped || mReportNextDraw); 4025 boolean triggerGlobalLayoutListener = didLayout 4026 || mAttachInfo.mRecomputeGlobalAttributes; 4027 if (didLayout) { 4028 //#ifdef OPLUS_EXTENSION_HOOK 4029 //Jie.Zhang@ANDROID.PERFORMANCE, 2021/12/14, Add for quality information update 4030 final long startTime = SystemClock.uptimeMillis(); 4031 performLayout(lp, mWidth, mHeight); 4032 ExtLoader.type(IOplusJankMonitorExt.class).create().setLaunchStageTime(ActivityThread.currentProcessName(), "performLayout", startTime); 4033 //#endif /* OPLUS_EXTENSION_HOOK */ 4034 // #ifdef OPLUS_EXTENSION_HOOK 4035 // Guofu.Yang@ANDROID.WMS, 2018/02/23, [OSP-1634] add for Screen mode. 4036 mViewRootImplExt.setRefreshRateIfNeed((mView instanceof ViewGroup), mContext, 4037 mView, mWindow); 4038 // #endif /* OPLUS_EXTENSION_HOOK */ 4039 4040 // By this point all views have been sized and positioned 4041 // We can compute the transparent area 4042 4043 if ((host.mPrivateFlags & View.PFLAG_REQUEST_TRANSPARENT_REGIONS) != 0) { 4044 // start out transparent 4045 // TODO: AVOID THAT CALL BY CACHING THE RESULT? 4046 host.getLocationInWindow(mTmpLocation); 4047 mTransparentRegion.set(mTmpLocation[0], mTmpLocation[1], 4048 mTmpLocation[0] + host.mRight - host.mLeft, 4049 mTmpLocation[1] + host.mBottom - host.mTop); 4050 4051 host.gatherTransparentRegion(mTransparentRegion); 4052 if (mTranslator != null) { 4053 mTranslator.translateRegionInWindowToScreen(mTransparentRegion); 4054 } 4055 4056 if (!mTransparentRegion.equals(mPreviousTransparentRegion)) { 4057 mPreviousTransparentRegion.set(mTransparentRegion); 4058 mFullRedrawNeeded = true; 4059 // TODO: Ideally we would do this in prepareSurfaces, 4060 // but prepareSurfaces is currently working under 4061 // the assumption that we paused the render thread 4062 // via the WM relayout code path. We probably eventually 4063 // want to synchronize transparent region hint changes 4064 // with draws. 4065 SurfaceControl sc = getSurfaceControl(); 4066 if (sc.isValid()) { 4067 mTransaction.setTransparentRegionHint(sc, mTransparentRegion).apply(); 4068 } 4069 } 4070 } 4071 4072 if (DBG) { 4073 System.out.println("======================================"); 4074 System.out.println("performTraversals -- after setFrame"); 4075 host.debug(); 4076 } 4077 } 4078 4079 boolean didUseTransaction = false; 4080 // These callbacks will trigger SurfaceView SurfaceHolder.Callbacks and must be invoked 4081 // after the measure pass. If its invoked before the measure pass and the app modifies 4082 // the view hierarchy in the callbacks, we could leave the views in a broken state. 4083 if (surfaceCreated) { 4084 notifySurfaceCreated(mTransaction); 4085 didUseTransaction = true; 4086 } else if (surfaceReplaced) { 4087 notifySurfaceReplaced(mTransaction); 4088 didUseTransaction = true; 4089 } else if (surfaceDestroyed) { 4090 notifySurfaceDestroyed(); 4091 } 4092 4093 if (didUseTransaction) { 4094 applyTransactionOnDraw(mTransaction); 4095 } 4096 4097 if (triggerGlobalLayoutListener) { 4098 mAttachInfo.mRecomputeGlobalAttributes = false; 4099 mAttachInfo.mTreeObserver.dispatchOnGlobalLayout(); 4100 } 4101 4102 Rect contentInsets = null; 4103 Rect visibleInsets = null; 4104 Region touchableRegion = null; 4105 int touchableInsetMode = TOUCHABLE_INSETS_REGION; 4106 boolean computedInternalInsets = false; 4107 if (computesInternalInsets) { 4108 final ViewTreeObserver.InternalInsetsInfo insets = mAttachInfo.mGivenInternalInsets; 4109 4110 // Clear the original insets. 4111 insets.reset(); 4112 4113 // Compute new insets in place. 4114 mAttachInfo.mTreeObserver.dispatchOnComputeInternalInsets(insets); 4115 mAttachInfo.mHasNonEmptyGivenInternalInsets = !insets.isEmpty(); 4116 4117 // Tell the window manager. 4118 if (insetsPending || !mLastGivenInsets.equals(insets)) { 4119 mLastGivenInsets.set(insets); 4120 4121 // Translate insets to screen coordinates if needed. 4122 if (mTranslator != null) { 4123 contentInsets = mTranslator.getTranslatedContentInsets(insets.contentInsets); 4124 visibleInsets = mTranslator.getTranslatedVisibleInsets(insets.visibleInsets); 4125 touchableRegion = mTranslator.getTranslatedTouchableArea(insets.touchableRegion); 4126 } else { 4127 contentInsets = insets.contentInsets; 4128 visibleInsets = insets.visibleInsets; 4129 touchableRegion = insets.touchableRegion; 4130 } 4131 computedInternalInsets = true; 4132 } 4133 touchableInsetMode = insets.mTouchableInsets; 4134 } 4135 boolean needsSetInsets = computedInternalInsets; 4136 needsSetInsets |= !Objects.equals(mPreviousTouchableRegion, mTouchableRegion) && 4137 (mTouchableRegion != null); 4138 if (needsSetInsets) { 4139 if (mTouchableRegion != null) { 4140 if (mPreviousTouchableRegion == null) { 4141 mPreviousTouchableRegion = new Region(); 4142 } 4143 mPreviousTouchableRegion.set(mTouchableRegion); 4144 if (touchableInsetMode != TOUCHABLE_INSETS_REGION) { 4145 Log.e(mTag, "Setting touchableInsetMode to non TOUCHABLE_INSETS_REGION" + 4146 " from OnComputeInternalInsets, while also using setTouchableRegion" + 4147 " causes setTouchableRegion to be ignored"); 4148 } 4149 } else { 4150 mPreviousTouchableRegion = null; 4151 } 4152 if (contentInsets == null) contentInsets = new Rect(0,0,0,0); 4153 if (visibleInsets == null) visibleInsets = new Rect(0,0,0,0); 4154 if (touchableRegion == null) { 4155 touchableRegion = mTouchableRegion; 4156 } else if (touchableRegion != null && mTouchableRegion != null) { 4157 touchableRegion.op(touchableRegion, mTouchableRegion, Region.Op.UNION); 4158 } 4159 try { 4160 mWindowSession.setInsets(mWindow, touchableInsetMode, 4161 contentInsets, visibleInsets, touchableRegion); 4162 } catch (RemoteException e) { 4163 throw e.rethrowFromSystemServer(); 4164 } 4165 } else if (mTouchableRegion == null && mPreviousTouchableRegion != null) { 4166 mPreviousTouchableRegion = null; 4167 try { 4168 mWindowSession.clearTouchableRegion(mWindow); 4169 } catch (RemoteException e) { 4170 throw e.rethrowFromSystemServer(); 4171 } 4172 } 4173 4174 if (mFirst) { 4175 if (sAlwaysAssignFocus || !isInTouchMode()) { 4176 // handle first focus request 4177 if (DEBUG_INPUT_RESIZE) { 4178 Log.v(mTag, "First: mView.hasFocus()=" + mView.hasFocus()); 4179 } 4180 if (mView != null) { 4181 if (!mView.hasFocus()) { 4182 mView.restoreDefaultFocus(); 4183 if (DEBUG_INPUT_RESIZE) { 4184 Log.v(mTag, "First: requested focused view=" + mView.findFocus()); 4185 } 4186 } else { 4187 if (DEBUG_INPUT_RESIZE) { 4188 Log.v(mTag, "First: existing focused view=" + mView.findFocus()); 4189 } 4190 } 4191 } 4192 } else { 4193 // Some views (like ScrollView) won't hand focus to descendants that aren't within 4194 // their viewport. Before layout, there's a good change these views are size 0 4195 // which means no children can get focus. After layout, this view now has size, but 4196 // is not guaranteed to hand-off focus to a focusable child (specifically, the edge- 4197 // case where the child has a size prior to layout and thus won't trigger 4198 // focusableViewAvailable). 4199 View focused = mView.findFocus(); 4200 if (focused instanceof ViewGroup 4201 && ((ViewGroup) focused).getDescendantFocusability() 4202 == ViewGroup.FOCUS_AFTER_DESCENDANTS) { 4203 focused.restoreDefaultFocus(); 4204 } 4205 } 4206 //#ifdef OPLUS_EXTENSION_HOOK 4207 //Honzhu@ANDROID.VIEW, 2020/07/20, add for Optimize app startup speed 4208 mViewRootImplExt.checkIsFragmentAnimUI(); 4209 //#endif /* OPLUS_EXTENSION_HOOK */ 4210 } 4211 4212 final boolean changedVisibility = (viewVisibilityChanged || mFirst) && isViewVisible; 4213 if (changedVisibility) { 4214 maybeFireAccessibilityWindowStateChangedEvent(); 4215 } 4216 4217 mFirst = false; 4218 mWillDrawSoon = false; 4219 mNewSurfaceNeeded = false; 4220 mViewVisibility = viewVisibility; 4221 4222 final boolean hasWindowFocus = mAttachInfo.mHasWindowFocus && isViewVisible; 4223 mImeFocusController.onTraversal(hasWindowFocus, mWindowAttributes); 4224 4225 if ((relayoutResult & WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0) { 4226 reportNextDraw("first_relayout"); 4227 } 4228 4229 mCheckIfCanDraw = isSyncRequest || cancelDraw; 4230 4231 boolean cancelDueToPreDrawListener = mAttachInfo.mTreeObserver.dispatchOnPreDraw(); 4232 boolean cancelAndRedraw = cancelDueToPreDrawListener 4233 || (cancelDraw && mDrewOnceForSync); 4234 //#ifdef OPLUS_EXTENSION_HOOK 4235 //Guofu.Yang@@ANDROID.WMS 2023/03/14,Add for fixed bug 5268134,5132324 4236 cancelAndRedraw = mViewRootImplExt.cancelAndRedraw(mTag,cancelAndRedraw,isViewVisible,mSyncBuffer); 4237 //#endif // OPLUS_EXTENSION_HOOK 4238 4239 if (!cancelAndRedraw) { 4240 // A sync was already requested before the WMS requested sync. This means we need to 4241 // sync the buffer, regardless if WMS wants to sync the buffer. 4242 if (mActiveSurfaceSyncGroup != null) { 4243 mSyncBuffer = true; 4244 } 4245 4246 createSyncIfNeeded(); 4247 notifyDrawStarted(isInWMSRequestedSync()); 4248 mDrewOnceForSync = true; 4249 4250 // If the active SSG is also requesting to sync a buffer, the following needs to happen 4251 // 1. Ensure we keep track of the number of active syncs to know when to disable RT 4252 // RT animations that conflict with syncing a buffer. 4253 // 2. Add a safeguard SSG to prevent multiple SSG that sync buffers from being submitted 4254 // out of order. 4255 if (mActiveSurfaceSyncGroup != null && mSyncBuffer) { 4256 updateSyncInProgressCount(mActiveSurfaceSyncGroup); 4257 safeguardOverlappingSyncs(mActiveSurfaceSyncGroup); 4258 } 4259 } 4260 // #ifdef OPLUS_FEATURE_EXTENSION_HOOKS 4261 // Bard.Zhang@Android.UIFramework 2023-10-30 Add for : debug 6429918 4262 else { 4263 Log.w(TAG, "cancelAndRedraw cancelDueToPreDrawListener " + cancelDueToPreDrawListener 4264 + " cancelDraw " + cancelDraw 4265 + " mDrewOnceForSync " + mDrewOnceForSync 4266 + " isSyncRequest " + isSyncRequest); 4267 } 4268 // #endif /* OPLUS_FEATURE_EXTENSION_HOOKS */ 4269 4270 if (!isViewVisible) { 4271 mLastPerformTraversalsSkipDrawReason = "view_not_visible"; 4272 if (mPendingTransitions != null && mPendingTransitions.size() > 0) { 4273 for (int i = 0; i < mPendingTransitions.size(); ++i) { 4274 mPendingTransitions.get(i).endChangingAnimations(); 4275 } 4276 mPendingTransitions.clear(); 4277 } 4278 4279 if (mActiveSurfaceSyncGroup != null) { 4280 mActiveSurfaceSyncGroup.markSyncReady(); 4281 } 4282 } else if (cancelAndRedraw) { 4283 mLastPerformTraversalsSkipDrawReason = cancelDueToPreDrawListener 4284 ? "predraw_" + mAttachInfo.mTreeObserver.getLastDispatchOnPreDrawCanceledReason() 4285 : "cancel_" + cancelReason; 4286 // Try again 4287 scheduleTraversals(); 4288 } else { 4289 if (mPendingTransitions != null && mPendingTransitions.size() > 0) { 4290 for (int i = 0; i < mPendingTransitions.size(); ++i) { 4291 mPendingTransitions.get(i).startChangingAnimations(); 4292 } 4293 mPendingTransitions.clear(); 4294 } 4295 if (!performDraw(mActiveSurfaceSyncGroup) && mActiveSurfaceSyncGroup != null) { 4296 mActiveSurfaceSyncGroup.markSyncReady(); 4297 } 4298 } 4299 4300 if (mAttachInfo.mContentCaptureEvents != null) { 4301 notifyContentCaptureEvents(); 4302 } 4303 mIsInTraversal = false; 4304 mRelayoutRequested = false; 4305 4306 if (!cancelAndRedraw) { 4307 mReportNextDraw = false; 4308 mLastReportNextDrawReason = null; 4309 mActiveSurfaceSyncGroup = null; 4310 mSyncBuffer = false; 4311 if (isInWMSRequestedSync()) { 4312 mWmsRequestSyncGroup.markSyncReady(); 4313 mWmsRequestSyncGroup = null; 4314 mWmsRequestSyncGroupState = WMS_SYNC_NONE; 4315 } 4316 } 4317 4318 // #ifdef OPLUS_FEATURE_VIEW_DEBUG 4319 // Bard.Zhang@Android.UIFramework 2023-01-28 Add for : view debug 4320 mViewRootImplExt.markOnPerformTraversalsEnd(host); 4321 // #endif /* OPLUS_FEATURE_VIEW_DEBUG */ 4322 4323 // #ifdef OPLUS_EXTENSION_HOOK 4324 // yanliang@Android.Performance 2023-02-13 Add for Optimize sliding effect 4325 mViewRootImplExt.checkPendingBufferCountSetting(mSurface); 4326 // #endif /*OPLUS_EXTENSION_HOOK*/ 4327 } 4328 解释里面的每行代码
09-24
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值