草庐IT

异步线程里的日志不好追踪?小支一招,轻松搞定!

buguge - Keep it simple,stupid 2023-04-16 原文

众所周知,通过唯一的链路id来追踪一次请求的所有日志,对于排查生产问题来说,会是非常给力的。
这个比较容易实现。我之前的博客也有多次提及 ▄︻┻┳═一 https://www.cnblogs.com/buguge/tag/日志链路追踪/
那么,如果涉及到异步线程处理的话,我们知道,由于异步线程与工作线程是两个不同的线程,因此,这时的线程名会发生变化。一次请求的完整日志就无法通过唯一的标识来过滤了。

 

有没有办法呢?
问题即答案。当然是有的。

 

线程是用来执行任务的,任务是一段程序代码的封装。在java中,任务通过 java.lang.Runnable 来表示。使用方面,我们可以自己定义一个实现Runnable的任务类,也可以用lambda表达式的方式直接使用Runnable,来作为线程或线程池的参数。

 

自定义实现了Runnable的类来使用异步线程,先贴demo代码。

实现了Runnable接口的类--MyTask:

package com.emaxcard.test;

import lombok.extern.slf4j.Slf4j;

@Slf4j
public class MyTask implements Runnable {
    String flag;

    public MyTask(String flag) {
        this.flag = flag;
    }

    @Override
    public void run() {
        log.info("这是异步线程里的日志。参数flag={}", flag);
    }
}
View Code

主线程测试类:

package com.emaxcard.test;

import lombok.extern.slf4j.Slf4j;

@Slf4j
public class RunnableTest {
    public static void main(String[] args) {
        log.info("主线程begin");
        new Thread(new MyTask("参数值test")).start();
        log.info("主线程end");
    }

}
View Code

 

打印出来的log如下:
16:11:27.613 [main] INFO com.emaxcard.test.RunnableTest - 主线程begin
16:11:27.629 [main] INFO com.emaxcard.test.RunnableTest - 主线程end
16:11:27.630 [Thread-0] INFO com.emaxcard.test.MyTask - 这是异步线程里的日志。参数flag=参数值test

我们希望看到的日志是:
16:11:27.613 [main] INFO com.emaxcard.test.RunnableTest - 主线程begin
16:11:27.629 [main] INFO com.emaxcard.test.RunnableTest - 主线程end
16:11:27.630 [main] INFO com.emaxcard.test.MyTask - 这是异步线程里的日志。参数flag=参数值test

小支一招,轻松搞定。给 MyTask 加点料,代码如下。这样就能实现?是的,因为构造MyTask对象的操作发生在当前工作线程,也就是说,MyTask构造器的代码是在工作线程里执行的。没错,正是基于这一点。

@Slf4j
public class MyTask implements Runnable {
    String flag;
    final String threadName;

    public MyTask(String flag) {
        this.flag = flag;
        this.threadName = Thread.currentThread().getName();
    }

    @Override
    public void run() {
        Thread.currentThread().setName(threadName);
        log.info("这是异步线程里的日志。参数flag={}", flag);
    }
}

 

同样,用lambda表达式使用Runnable来使用异步线程,传递线程名,也很简单。贴出来上面的 RunnableTest 的代码。

@Slf4j
public class RunnableTest {
    public static void main(String[] args) {
        log.info("主线程begin");
        final String name = Thread.currentThread().getName();
        new Thread(new Runnable() {
            @Override
            public void run() {
                Thread.currentThread().setName(name);
                log.info("这是异步线程里的日志");
            }
        }).start();
        log.info("主线程end");
    }

}

 

有关异步线程里的日志不好追踪?小支一招,轻松搞定!的更多相关文章

  1. ruby-on-rails - 如何在 ruby​​ 中使用两个参数异步运行 exe? - 2

    exe应该在我打开页面时运行。异步进程需要运行。有什么方法可以在ruby​​中使用两个参数异步运行exe吗?我已经尝试过ruby​​命令-system()、exec()但它正在等待过程完成。我需要用参数启动exe,无需等待进程完成是否有任何ruby​​gems会支持我的问题? 最佳答案 您可以使用Process.spawn和Process.wait2:pid=Process.spawn'your.exe','--option'#Later...pid,status=Process.wait2pid您的程序将作为解释器的子进程执行。除

  2. ruby - RuntimeError(自动加载常量 Apps 多线程时检测到循环依赖 - 2

    我收到这个错误:RuntimeError(自动加载常量Apps时检测到循环依赖当我使用多线程时。下面是我的代码。为什么会这样?我尝试多线程的原因是因为我正在编写一个HTML抓取应用程序。对Nokogiri::HTML(open())的调用是一个同步阻塞调用,需要1秒才能返回,我有100,000多个页面要访问,所以我试图运行多个线程来解决这个问题。有更好的方法吗?classToolsController0)app.website=array.join(',')putsapp.websiteelseapp.website="NONE"endapp.saveapps=Apps.order("

  3. 屏幕录制为什么没声音?检查这2项,轻松解决 - 2

    相信很多人在录制视频的时候都会遇到各种各样的问题,比如录制的视频没有声音。屏幕录制为什么没声音?今天小编就和大家分享一下如何录制音画同步视频的具体操作方法。如果你有录制的视频没有声音,你可以试试这个方法。 一、检查是否打开电脑系统声音相信很多小伙伴在录制视频后会发现录制的视频没有声音,屏幕录制为什么没声音?如果当时没有打开音频录制,则录制好的视频是没有声音的。因此,建议在录制前进行检查。屏幕上没有声音,很可能是因为你的电脑系统的声音被禁止了。您只需打开电脑系统的声音,即可录制音频和图画同步视频。操作方法:步骤1:点击电脑屏幕右下侧的“小喇叭”图案,在上方的选项中,选择“声音”。 步骤2:在“声

  4. ruby - 如何让Ruby捕获线程中的语法错误 - 2

    我正在尝试使用ruby​​编写一个双线程客户端,一个线程从套接字读取数据并将其打印出来,另一个线程读取本地数据并将其发送到远程服务器。我发现的问题是Ruby似乎无法捕获线程内的错误,这是一个示例:#!/usr/bin/rubyThread.new{loop{$stdout.puts"hi"abc.putsefsleep1}}loop{sleep1}显然,如果我在线程外键入abc.putsef,代码将永远不会运行,因为Ruby将报告“undefinedvariableabc”。但是,如果它在一个线程内,则没有错误报告。我的问题是,如何让Ruby捕获这样的错误?或者至少,报告线程中的错误?

  5. ruby - 如何在 ruby​​ 中运行后台线程? - 2

    我是ruby​​的新手,我认为重新构建一个我用C#编写的简单聊天程序是个好主意。我正在使用Ruby2.0.0MRI(Matz的Ruby实现)。问题是我想在服务器运行时为简单的服务器命令提供I/O。这是从示例中获取的服务器。我添加了使用gets()获取输入的命令方法。我希望此方法在后台作为线程运行,但该线程正在阻塞另一个线程。require'socket'#Getsocketsfromstdlibserver=TCPServer.open(2000)#Sockettolistenonport2000defcommandsx=1whilex==1exitProgram=gets.chomp

  6. ruby-on-rails - 在 Ruby on Rails 中发送响应之前如何等待多个异步操作完成? - 2

    在我做的一些网络开发中,我有多个操作开始,比如对外部API的GET请求,我希望它们同时开始,因为一个不依赖另一个的结果。我希望事情能够在后台运行。我找到了concurrent-rubylibrary这似乎运作良好。通过将其混合到您创建的类中,该类的方法具有在后台线程上运行的异步版本。这导致我编写如下代码,其中FirstAsyncWorker和SecondAsyncWorker是我编写的类,我在其中混合了Concurrent::Async模块,并编写了一个名为“work”的方法来发送HTTP请求:defindexop1_result=FirstAsyncWorker.new.async.

  7. ruby - Rails 开发服务器、PDFKit 和多线程 - 2

    我有一个使用PDFKit呈现网页的pdf版本的Rails应用程序。我使用Thin作为开发服务器。问题是当我处于开发模式时。当我使用“bundleexecrailss”启动我的服务器并尝试呈现任何PDF时,整个过程会陷入僵局,因为当您呈现PDF时,会向服务器请求一些额外的资源,如图像和css,看起来只有一个线程.如何配置Rails开发服务器以运行多个工作线程?非常感谢。 最佳答案 我找到的最简单的解决方案是unicorn.geminstallunicorn创建一个unicorn.conf:worker_processes3然后使用它:

  8. ruby - Sinatra 中的全局救援和日志记录异常 - 2

    如何在出现异常时指定全局救援,如果您将Sinatra用于API或应用程序,您将如何处理日志记录? 最佳答案 404可以在not_found方法的帮助下处理,例如:not_founddo'Sitedoesnotexist.'end500s可以通过调用带有block的错误方法来处理,例如:errordo"Applicationerror.Plstrylater."end错误的详细信息可以通过request.env中的sinatra.error访问,如下所示:errordo'Anerroroccured:'+request.env['si

  9. ruby-on-rails - 使用 Ruby 标准 Logger 每天只创建一个日志 - 2

    我正在使用ruby​​标准记录器,我想要每天轮换一次,所以在我的代码中我有:Logger.new("#{$ROOT_PATH}/log/errors.log",'daily')它运行完美,但它创建了两个文件errors.log.20130217和errors.log.20130217.1。如何强制它每天只创建一个文件? 最佳答案 您的代码对于长时间运行的应用程序是正确的。发生的事情是您在给定的一天多次运行代码。第一次运行时,Ruby会创建一个日志文件“errors.log”。当日期改变时,Ruby将文件重命名为“errors.log

  10. ruby - Cucumber/Savon 省略或删除日志输出 - 2

    在运行Cucumber测试时,我得到(除了测试结果)大量调试/日志相关的输出形式:D,[2013-03-06T12:21:38.911829#49031]DEBUG--:SOAPrequest:D,[2013-03-06T12:21:38.911919#49031]DEBUG--:Pragma:no-cache,SOAPAction:"",Content-Type:text/xml;charset=UTF-8,Content-Length:1592W,[2013-03-06T12:21:38.912360#49031]WARN--:HTTPIexecutesHTTPPOSTusingt

随机推荐