View Javadoc
1   /*
2    * Copyright 2021 The Netty Project
3    *
4    * The Netty Project licenses this file to you under the Apache License,
5    * version 2.0 (the "License"); you may not use this file except in compliance
6    * with the License. You may obtain a copy of the License at:
7    *
8    *   https://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13   * License for the specific language governing permissions and limitations
14   * under the License.
15   */
16  package io.netty5.buffer.api;
17  
18  import io.netty5.util.internal.logging.InternalLogger;
19  import io.netty5.util.internal.logging.InternalLoggerFactory;
20  
21  import java.util.function.Consumer;
22  
23  /**
24   * The {@link LoggingLeakCallback} can be {@linkplain MemoryManager#onLeakDetected(Consumer) installed} to enable
25   * logging output when a leak is detected.
26   * <p>
27   * The logging output will be done with the {@code ERROR} level.
28   * <p>
29   * Note that asynchronous and fast logging should be preferred, since the callback may run inside a cleaner-thread.
30   */
31  public final class LoggingLeakCallback implements Consumer<LeakInfo> {
32      private static final InternalLogger logger = InternalLoggerFactory.getInstance(LoggingLeakCallback.class);
33      private static final LoggingLeakCallback instance = new LoggingLeakCallback();
34  
35      /**
36       * Get an instance of the {@link LoggingLeakCallback}.
37       *
38       * @return An instance of the {@link LoggingLeakCallback} class.
39       */
40      public static LoggingLeakCallback getInstance() {
41          return instance;
42      }
43  
44      private LoggingLeakCallback() {
45      }
46  
47      @Override
48      public void accept(LeakInfo leakInfo) {
49          if (logger.isErrorEnabled()) {
50              String message = "LEAK: Object \"" + leakInfo.objectDescription() + "\" was not property closed before " +
51                               "it was garbage collected. " +
52                               "A life-cycle back-trace (if any) is attached as suppressed exceptions. " +
53                               "See https://netty.io/wiki/reference-counted-objects.html for more information.";
54              logger.error(message, LeakReport.reportFor(leakInfo));
55          }
56      }
57  
58      private static final class LeakReport extends Throwable {
59          private static final long serialVersionUID = -1894217374238341652L;
60  
61          static LeakReport reportFor(LeakInfo info) {
62              LeakReport report = new LeakReport();
63              info.forEach(tracePoint -> report.addSuppressed(tracePoint.traceback()));
64              return report;
65          }
66  
67          private LeakReport() {
68              super("Object life-cycle trace:", null, true, false);
69          }
70      }
71  }