Android 系统日志(Log) JNI实现流程源码分析

1、JNI概述

Java Native Interface (JNI) 是一种编程框架,使得Java代码能够与用其他编程语言(如C和C++)编写的本地代码进行交互。JNI允许Java代码调用本地代码的函数,也允许本地代码调用Java代码的函数。下面是对JNI机制的详细概述,包括其基本原理、工作流程、常见用途和示例代码。

1.1. JNI的基本原理

JNI的核心原理是通过一个标准化的接口,使得Java虚拟机(JVM)和本地代码可以互相通信和操作。JNI提供了一套函数,这些函数可以在本地代码中使用,以便与JVM交互,例如创建和操作Java对象,调用Java方法,处理异常等。

1.2. JNI的工作流程

使用JNI的工作流程通常包括以下几个步骤:

1、声明本地方法:在Java类中声明native方法。
2、加载本地库:在Java类中加载包含本地方法实现的库。
3、生成头文件:使用javac和javah工具生成包含native方法声明的头文件。
4、实现本地方法:在C/C++中实现native方法。
5、编译本地库:编译C/C++代码生成共享库。
6、调用本地方法:在Java代码中调用native方法。

1.3. JNI的常见用途

JNI常用于以下几种场景:

1、性能优化:将性能关键的部分用C/C++实现,以提高执行效率。
2、访问底层系统资源:访问操作系统的底层功能或硬件资源。
3、重用现有库:调用已有的C/C++库或API。
4、实现平台特定功能:在跨平台应用中实现特定平台的功能。

2、JNI的优缺点

2.1、优点

1、性能优化:可以使用高效的本地代码,尤其是在性能关键的部分。
2、硬件访问:能够直接访问Java API不提供的底层硬件功能。
3、代码复用:重用现有的C/C++库,无需重新实现复杂的逻辑。
4、多语言互操作:可以在同一个应用中使用多种编程语言,各取所长。

2.2、缺点

1、复杂性增加:引入了额外的复杂性,需要了解C/C++和JNI API。
2、平台依赖性:本地代码需要针对不同的平台编译,增加了维护成本。
3、内存管理:需要手动管理内存,容易出现内存泄漏和指针错误。
4、调试困难:调试JNI代码比纯Java代码困难,需要使用特定的工具和方法。

3、JNI的使用场景

1、性能优化:在需要大量计算或复杂逻辑的地方使用本地代码。
2、硬件功能:访问摄像头、传感器等底层硬件功能。
3、现有库:使用已有的C/C++库,例如图像处理库、加密库等。
4、跨语言调用:在需要与其他编程语言互操作时,例如从Java调用C++代码。

4、Android日志系统概述

Android日志系统主要由Log类和__android_log_print等C/C++函数组成,提供了记录调试信息的功能。Java层的日志API(如android.util.Log)最终调用的是本地日志函数,这些函数将日志消息写入系统日志缓冲区。

要深入了解Android Log的JNI实现流程,我们需要从JNI机制、Android日志系统、JNI方法实现及其相互配合的细节等方面进行详细分析。

5、本地方法声明(关键字native)

frameworks\base\core\java\android\util\Log.java

 /*** Checks to see whether or not a log for the specified tag is loggable at the specified level.**  The default level of any tag is set to INFO. This means that any level above and including*  INFO will be logged. Before you make any calls to a logging method you should check to see*  if your tag should be logged. You can change the default level by setting a system property:*      'setprop log.tag.<YOUR_LOG_TAG> <LEVEL>'*  Where level is either VERBOSE, DEBUG, INFO, WARN, ERROR, ASSERT, or SUPPRESS. SUPPRESS will*  turn off all logging for your tag. You can also create a local.prop file that with the*  following in it:*      'log.tag.<YOUR_LOG_TAG>=<LEVEL>'*  and place that in /data/local.prop.** @param tag The tag to check.* @param level The level to check.* @return Whether or not that this is allowed to be logged.* @throws IllegalArgumentException is thrown if the tag.length() > 23.*/public static native boolean isLoggable(String tag, int level);

在这里插入图片描述

/*** Low-level logging call.* @param priority The priority/type of this log message* @param tag Used to identify the source of a log message.  It usually identifies*        the class or activity where the log call occurs.* @param msg The message you would like logged.* @return The number of bytes written.*/public static int println(int priority, String tag, String msg) {return println_native(LOG_ID_MAIN, priority, tag, msg);}/** @hide */ public static final int LOG_ID_MAIN = 0;/** @hide */ public static final int LOG_ID_RADIO = 1;/** @hide */ public static final int LOG_ID_EVENTS = 2;/** @hide */ public static final int LOG_ID_SYSTEM = 3;/** @hide */ public static native int println_native(int bufID,int priority, String tag, String msg);

在这里插入图片描述

6、本地方法实现

frameworks\base\core\jni\android_util_Log.cpp

static jboolean android_util_Log_isLoggable(JNIEnv* env, jobject clazz, jstring tag, jint level)
{if (tag == NULL) {return false;}const char* chars = env->GetStringUTFChars(tag, NULL);if (!chars) {return false;}jboolean result = false;if ((strlen(chars)+sizeof(LOG_NAMESPACE)) > PROPERTY_KEY_MAX) {char buf2[200];snprintf(buf2, sizeof(buf2), "Log tag \"%s\" exceeds limit of %d characters\n",chars, PROPERTY_KEY_MAX - sizeof(LOG_NAMESPACE));jniThrowException(env, "java/lang/IllegalArgumentException", buf2);} else {result = isLoggable(chars, level);}env->ReleaseStringUTFChars(tag, chars);return result;
}

在这里插入图片描述

/** In class android.util.Log:*  public static native int println_native(int buffer, int priority, String tag, String msg)*/
static jint android_util_Log_println_native(JNIEnv* env, jobject clazz,jint bufID, jint priority, jstring tagObj, jstring msgObj)
{const char* tag = NULL;const char* msg = NULL;if (msgObj == NULL) {jniThrowNullPointerException(env, "println needs a message");return -1;}if (bufID < 0 || bufID >= LOG_ID_MAX) {jniThrowNullPointerException(env, "bad bufID");return -1;}if (tagObj != NULL)tag = env->GetStringUTFChars(tagObj, NULL);msg = env->GetStringUTFChars(msgObj, NULL);int res = __android_log_buf_write(bufID, (android_LogPriority)priority, tag, msg);if (tag != NULL)env->ReleaseStringUTFChars(tagObj, tag);env->ReleaseStringUTFChars(msgObj, msg);return res;
}

在这里插入图片描述

7、虚拟机里面动态注册

frameworks\base\core\jni\AndroidRuntime.cpp

/** Register android native functions with the VM.*/
/*static*/ int AndroidRuntime::startReg(JNIEnv* env)
{/** This hook causes all future threads created in this process to be* attached to the JavaVM.  (This needs to go away in favor of JNI* Attach calls.)*/androidSetCreateThreadFunc((android_create_thread_fn) javaCreateThreadEtc);ALOGV("--- registering native functions ---\n");/** Every "register" function calls one or more things that return* a local reference (e.g. FindClass).  Because we haven't really* started the VM yet, they're all getting stored in the base frame* and never released.  Use Push/Pop to manage the storage.*/env->PushLocalFrame(200);if (register_jni_procs(gRegJNI, NELEM(gRegJNI), env) < 0) {env->PopLocalFrame(NULL);return -1;}env->PopLocalFrame(NULL);//createJavaThread("fubar", quickTest, (void*) "hello");return 0;
}

在这里插入图片描述

static int register_jni_procs(const RegJNIRec array[], size_t count, JNIEnv* env)
{for (size_t i = 0; i < count; i++) {if (array[i].mProc(env) < 0) {
#ifndef NDEBUGALOGD("----------!!! %s failed to load\n", array[i].mName);
#endifreturn -1;}}return 0;
}

在这里插入图片描述

注意传入进来的参数

static const RegJNIRec gRegJNI[] = {REG_JNI(register_android_debug_JNITest),REG_JNI(register_com_android_internal_os_RuntimeInit),REG_JNI(register_android_os_SystemClock),REG_JNI(register_android_util_EventLog),REG_JNI(register_android_util_Log),REG_JNI(register_android_util_FloatMath),REG_JNI(register_android_text_format_Time),REG_JNI(register_android_content_AssetManager),REG_JNI(register_android_content_StringBlock),REG_JNI(register_android_content_XmlBlock),REG_JNI(register_android_emoji_EmojiFactory),REG_JNI(register_android_text_AndroidCharacter),REG_JNI(register_android_text_AndroidBidi),REG_JNI(register_android_view_InputDevice),REG_JNI(register_android_view_KeyCharacterMap),REG_JNI(register_android_os_Process),REG_JNI(register_android_os_SystemProperties),REG_JNI(register_android_os_Binder),REG_JNI(register_android_os_Parcel),REG_JNI(register_android_view_DisplayEventReceiver),REG_JNI(register_android_nio_utils),REG_JNI(register_android_graphics_PixelFormat),REG_JNI(register_android_graphics_Graphics),REG_JNI(register_android_view_GLES20DisplayList),REG_JNI(register_android_view_GLES20Canvas),REG_JNI(register_android_view_HardwareRenderer),REG_JNI(register_android_view_Surface),REG_JNI(register_android_view_SurfaceSession),REG_JNI(register_android_view_TextureView),REG_JNI(register_com_google_android_gles_jni_EGLImpl),REG_JNI(register_com_google_android_gles_jni_GLImpl),REG_JNI(register_android_opengl_jni_EGL14),REG_JNI(register_android_opengl_jni_GLES10),REG_JNI(register_android_opengl_jni_GLES10Ext),REG_JNI(register_android_opengl_jni_GLES11),REG_JNI(register_android_opengl_jni_GLES11Ext),REG_JNI(register_android_opengl_jni_GLES20),REG_JNI(register_android_graphics_Bitmap),REG_JNI(register_android_graphics_BitmapFactory),REG_JNI(register_android_graphics_BitmapRegionDecoder),REG_JNI(register_android_graphics_Camera),REG_JNI(register_android_graphics_Canvas),REG_JNI(register_android_graphics_ColorFilter),REG_JNI(register_android_graphics_DrawFilter),REG_JNI(register_android_graphics_Interpolator),REG_JNI(register_android_graphics_LayerRasterizer),REG_JNI(register_android_graphics_MaskFilter),REG_JNI(register_android_graphics_Matrix),REG_JNI(register_android_graphics_Movie),REG_JNI(register_android_graphics_NinePatch),REG_JNI(register_android_graphics_Paint),REG_JNI(register_android_graphics_Path),REG_JNI(register_android_graphics_PathMeasure),REG_JNI(register_android_graphics_PathEffect),REG_JNI(register_android_graphics_Picture),REG_JNI(register_android_graphics_PorterDuff),REG_JNI(register_android_graphics_Rasterizer),REG_JNI(register_android_graphics_Region),REG_JNI(register_android_graphics_Shader),REG_JNI(register_android_graphics_SurfaceTexture),REG_JNI(register_android_graphics_Typeface),REG_JNI(register_android_graphics_Xfermode),REG_JNI(register_android_graphics_YuvImage),REG_JNI(register_android_database_CursorWindow),REG_JNI(register_android_database_SQLiteConnection),REG_JNI(register_android_database_SQLiteGlobal),REG_JNI(register_android_database_SQLiteDebug),REG_JNI(register_android_os_Debug),REG_JNI(register_android_os_FileObserver),REG_JNI(register_android_os_FileUtils),REG_JNI(register_android_os_MessageQueue),REG_JNI(register_android_os_ParcelFileDescriptor),REG_JNI(register_android_os_SELinux),REG_JNI(register_android_os_Trace),REG_JNI(register_android_os_UEventObserver),REG_JNI(register_android_net_LocalSocketImpl),REG_JNI(register_android_net_NetworkUtils),REG_JNI(register_android_net_TrafficStats),REG_JNI(register_android_net_wifi_WifiManager),REG_JNI(register_android_os_MemoryFile),REG_JNI(register_com_android_internal_os_ZygoteInit),REG_JNI(register_android_hardware_Camera),REG_JNI(register_android_hardware_SensorManager),REG_JNI(register_android_hardware_SerialPort),REG_JNI(register_android_hardware_UsbDevice),REG_JNI(register_android_hardware_UsbDeviceConnection),REG_JNI(register_android_hardware_UsbRequest),REG_JNI(register_android_media_AudioRecord),REG_JNI(register_android_media_AudioSystem),REG_JNI(register_android_media_AudioTrack),REG_JNI(register_android_media_JetPlayer),REG_JNI(register_android_media_RemoteDisplay),REG_JNI(register_android_media_ToneGenerator),REG_JNI(register_android_opengl_classes),REG_JNI(register_android_server_NetworkManagementSocketTagger),REG_JNI(register_android_server_Watchdog),REG_JNI(register_android_ddm_DdmHandleNativeHeap),REG_JNI(register_android_backup_BackupDataInput),REG_JNI(register_android_backup_BackupDataOutput),REG_JNI(register_android_backup_FileBackupHelperBase),REG_JNI(register_android_backup_BackupHelperDispatcher),REG_JNI(register_android_app_backup_FullBackup),REG_JNI(register_android_app_ActivityThread),REG_JNI(register_android_app_NativeActivity),REG_JNI(register_android_view_InputChannel),REG_JNI(register_android_view_InputEventReceiver),REG_JNI(register_android_view_KeyEvent),REG_JNI(register_android_view_MotionEvent),REG_JNI(register_android_view_PointerIcon),REG_JNI(register_android_view_VelocityTracker),REG_JNI(register_android_content_res_ObbScanner),REG_JNI(register_android_content_res_Configuration),REG_JNI(register_android_animation_PropertyValuesHolder),REG_JNI(register_com_android_internal_content_NativeLibraryHelper),
};

在这里插入图片描述

看到register_android_util_Log了吧

int register_android_util_Log(JNIEnv* env)
{jclass clazz = env->FindClass("android/util/Log");if (clazz == NULL) {ALOGE("Can't find android/util/Log");return -1;}levels.verbose = env->GetStaticIntField(clazz, env->GetStaticFieldID(clazz, "VERBOSE", "I"));levels.debug = env->GetStaticIntField(clazz, env->GetStaticFieldID(clazz, "DEBUG", "I"));levels.info = env->GetStaticIntField(clazz, env->GetStaticFieldID(clazz, "INFO", "I"));levels.warn = env->GetStaticIntField(clazz, env->GetStaticFieldID(clazz, "WARN", "I"));levels.error = env->GetStaticIntField(clazz, env->GetStaticFieldID(clazz, "ERROR", "I"));levels.assert = env->GetStaticIntField(clazz, env->GetStaticFieldID(clazz, "ASSERT", "I"));return AndroidRuntime::registerNativeMethods(env, "android/util/Log", gMethods, NELEM(gMethods));
}

在这里插入图片描述
动态注册完成,那共享库(.so)是在哪里加载的呢?

8、共享库加载

看本地实现文件的mk:frameworks\base\core\jni

LOCAL_SRC_FILES:= \AndroidRuntime.cpp \Time.cpp \com_android_internal_content_NativeLibraryHelper.cpp \com_google_android_gles_jni_EGLImpl.cpp \com_google_android_gles_jni_GLImpl.cpp.arm \android_app_NativeActivity.cpp \android_opengl_EGL14.cpp \android_opengl_GLES10.cpp \android_opengl_GLES10Ext.cpp \android_opengl_GLES11.cpp \android_opengl_GLES11Ext.cpp \android_opengl_GLES20.cpp \android_database_CursorWindow.cpp \android_database_SQLiteCommon.cpp \android_database_SQLiteConnection.cpp \android_database_SQLiteGlobal.cpp \android_database_SQLiteDebug.cpp \android_emoji_EmojiFactory.cpp \android_view_DisplayEventReceiver.cpp \android_view_Surface.cpp \android_view_SurfaceSession.cpp \android_view_TextureView.cpp \android_view_InputChannel.cpp \android_view_InputDevice.cpp \android_view_InputEventReceiver.cpp \android_view_KeyEvent.cpp \android_view_KeyCharacterMap.cpp \android_view_HardwareRenderer.cpp \android_view_GLES20DisplayList.cpp \android_view_GLES20Canvas.cpp \android_view_MotionEvent.cpp \android_view_PointerIcon.cpp \android_view_VelocityTracker.cpp \android_text_AndroidCharacter.cpp \android_text_AndroidBidi.cpp \android_os_Debug.cpp \android_os_FileUtils.cpp \android_os_MemoryFile.cpp \android_os_MessageQueue.cpp \android_os_ParcelFileDescriptor.cpp \android_os_Parcel.cpp \android_os_SELinux.cpp \android_os_SystemClock.cpp \android_os_SystemProperties.cpp \android_os_Trace.cpp \android_os_UEventObserver.cpp \android_net_LocalSocketImpl.cpp \android_net_NetUtils.cpp \android_net_TrafficStats.cpp \android_net_wifi_Wifi.cpp \android_nio_utils.cpp \android_text_format_Time.cpp \android_util_AssetManager.cpp \android_util_Binder.cpp \android_util_EventLog.cpp \android_util_Log.cpp \android_util_FloatMath.cpp \android_util_Process.cpp \android_util_StringBlock.cpp \android_util_XmlBlock.cpp \android/graphics/AutoDecodeCancel.cpp \android/graphics/Bitmap.cpp \android/graphics/BitmapFactory.cpp \android/graphics/Camera.cpp \android/graphics/Canvas.cpp \android/graphics/ColorFilter.cpp \android/graphics/DrawFilter.cpp \android/graphics/CreateJavaOutputStreamAdaptor.cpp \android/graphics/Graphics.cpp \android/graphics/HarfbuzzSkia.cpp \android/graphics/Interpolator.cpp \android/graphics/LayerRasterizer.cpp \android/graphics/MaskFilter.cpp \android/graphics/Matrix.cpp \android/graphics/Movie.cpp \android/graphics/NinePatch.cpp \android/graphics/NinePatchImpl.cpp \android/graphics/NinePatchPeeker.cpp \android/graphics/Paint.cpp \android/graphics/Path.cpp \android/graphics/PathMeasure.cpp \android/graphics/PathEffect.cpp \android_graphics_PixelFormat.cpp \android/graphics/Picture.cpp \android/graphics/PorterDuff.cpp \android/graphics/BitmapRegionDecoder.cpp \android/graphics/Rasterizer.cpp \android/graphics/Region.cpp \android/graphics/Shader.cpp \android/graphics/SurfaceTexture.cpp \android/graphics/TextLayout.cpp \android/graphics/TextLayoutCache.cpp \android/graphics/Typeface.cpp \android/graphics/Utils.cpp \android/graphics/Xfermode.cpp \android/graphics/YuvToJpegEncoder.cpp \android_media_AudioRecord.cpp \android_media_AudioSystem.cpp \android_media_AudioTrack.cpp \android_media_JetPlayer.cpp \android_media_RemoteDisplay.cpp \android_media_ToneGenerator.cpp \android_hardware_Camera.cpp \android_hardware_SensorManager.cpp \android_hardware_SerialPort.cpp \android_hardware_UsbDevice.cpp \android_hardware_UsbDeviceConnection.cpp \android_hardware_UsbRequest.cpp \android_debug_JNITest.cpp \android_util_FileObserver.cpp \android/opengl/poly_clip.cpp.arm \android/opengl/util.cpp.arm \android_server_NetworkManagementSocketTagger.cpp \android_server_Watchdog.cpp \android_ddm_DdmHandleNativeHeap.cpp \com_android_internal_os_ZygoteInit.cpp \android_backup_BackupDataInput.cpp \android_backup_BackupDataOutput.cpp \android_backup_FileBackupHelperBase.cpp \android_backup_BackupHelperDispatcher.cpp \android_app_backup_FullBackup.cpp \android_content_res_ObbScanner.cpp \android_content_res_Configuration.cpp \android_animation_PropertyValuesHolder.cpp

在这里插入图片描述

编译的共享库为:LOCAL_MODULE:= libandroid_runtime

ifeq ($(HAVE_SELINUX),true)
LOCAL_C_INCLUDES += external/libselinux/include
LOCAL_SHARED_LIBRARIES += libselinux
LOCAL_CFLAGS += -DHAVE_SELINUX
endif # HAVE_SELINUXifeq ($(USE_OPENGL_RENDERER),true)LOCAL_SHARED_LIBRARIES += libhwui
endifLOCAL_SHARED_LIBRARIES += \libdl
# we need to access the private Bionic header
# <bionic_tls.h> in com_google_android_gles_jni_GLImpl.cpp
LOCAL_CFLAGS += -I$(LOCAL_PATH)/../../../../bionic/libc/privateLOCAL_LDLIBS += -lpthread -ldlifeq ($(WITH_MALLOC_LEAK_CHECK),true)LOCAL_CFLAGS += -DMALLOC_LEAK_CHECK
endifLOCAL_MODULE:= libandroid_runtimeinclude $(BUILD_SHARED_LIBRARY)include $(call all-makefiles-under,$(LOCAL_PATH))

在这里插入图片描述

而共享库LOCAL_MODULE:= libandroid_runtime又被编译到另一个共享库:LOCAL_MODULE:= libandroid_servers

frameworks\base\services\jni:

LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)LOCAL_SRC_FILES:= \com_android_server_AlarmManagerService.cpp \com_android_server_BatteryService.cpp \com_android_server_input_InputApplicationHandle.cpp \com_android_server_input_InputManagerService.cpp \com_android_server_input_InputWindowHandle.cpp \com_android_server_LightsService.cpp \com_android_server_power_PowerManagerService.cpp \com_android_server_SerialService.cpp \com_android_server_SystemServer.cpp \com_android_server_UsbDeviceManager.cpp \com_android_server_UsbHostManager.cpp \com_android_server_VibratorService.cpp \com_android_server_location_GpsLocationProvider.cpp \com_android_server_connectivity_Vpn.cpp \onload.cppLOCAL_C_INCLUDES += \$(JNI_H_INCLUDE) \frameworks/base/services \frameworks/base/core/jni \external/skia/include/core \libcore/include \libcore/include/libsuspend \$(call include-path-for, libhardware)/hardware \$(call include-path-for, libhardware_legacy)/hardware_legacy \LOCAL_SHARED_LIBRARIES := \libandroid_runtime \libandroidfw \libcutils \libhardware \libhardware_legacy \libnativehelper \libsystem_server \libutils \libui \libinput \libskia \libgui \libusbhost \libsuspendifeq ($(WITH_MALLOC_LEAK_CHECK),true)LOCAL_CFLAGS += -DMALLOC_LEAK_CHECK
endifLOCAL_MODULE:= libandroid_serversinclude $(BUILD_SHARED_LIBRARY)

在这里插入图片描述

那就看共享库libandroid_servers是在那里加载的吧!
frameworks\base\services\java\com\android\server

public static void main(String[] args) {if (System.currentTimeMillis() < EARLIEST_SUPPORTED_TIME) {// If a device's clock is before 1970 (before 0), a lot of// APIs crash dealing with negative numbers, notably// java.io.File#setLastModified, so instead we fake it and// hope that time from cell towers or NTP fixes it// shortly.Slog.w(TAG, "System clock is before 1970; setting to 1970.");SystemClock.setCurrentTimeMillis(EARLIEST_SUPPORTED_TIME);}if (SamplingProfilerIntegration.isEnabled()) {SamplingProfilerIntegration.start();timer = new Timer();timer.schedule(new TimerTask() {@Overridepublic void run() {SamplingProfilerIntegration.writeSnapshot("system_server", null);}}, SNAPSHOT_INTERVAL, SNAPSHOT_INTERVAL);}// Mmmmmm... more memory!dalvik.system.VMRuntime.getRuntime().clearGrowthLimit();// The system server has to run all of the time, so it needs to be// as efficient as possible with its memory usage.VMRuntime.getRuntime().setTargetHeapUtilization(0.8f);System.loadLibrary("android_servers");init1(args);}

在这里插入图片描述
现在知道Log的JNI实现流程了吧?!!!

9、总结

JNI是Android开发中的一个重要工具,提供了Java代码与本地代码之间的桥梁。通过JNI,开发者可以在Java代码中调用高效的本地代码,访问特定硬件功能,以及重用现有的C/C++库。理解JNI的工作原理和使用方法,对于开发高性能、功能丰富的Android应用至关重要。

JNI(Java Native Interface)是Java平台的重要组成部分,允许Java代码与本地(通常是C/C++)代码进行互操作。在Android开发中,JNI广泛应用于性能优化、硬件访问以及重用现有的本地库。以下是关于JNI使用的总结,包括其优缺点、使用场景、开发步骤、注意事项以及最佳实践。

Android日志系统从应用程序层到JNI层、本地层、Logd守护进程,再到Logcat工具,形成了一个完整的日志记录与查看的流程。通过分析各个层级的代码实现,可以更深入地理解日志系统的设计和工作原理。这些组件紧密协作,提供了一个高效、可靠的日志记录和查看机制,帮助开发者调试和监控应用程序。

欢迎点赞|关注|收藏|评论,您的肯定是我创作的动力

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/16183.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【单片机】STM32F070F6P6 开发指南(一)STM32建立HAL工程

文章目录 一、基础入门二、工程初步建立三、HSE 和 LSE 时钟源设置四、时钟系统&#xff08;时钟树&#xff09;配置五、GPIO 功能引脚配置六、配置 Debug 选项七、生成工程源码八、生成工程源码九、用户程序下载 一、基础入门 f0 pack下载&#xff1a; https://www.keil.arm…

大模型应用:基于Golang实现GPT模型API调用

1.背景 当前OpenAI提供了开放接口&#xff0c;支持通过api的方式调用LLM进行文本推理、图片生成等能力&#xff0c;但目前官方只提供了Python SDK。为了后续更方便集成和应用&#xff0c;可以采用Golang对核心推理调用接口进行封装&#xff0c;提供模型调用能力。 2.相关准备…

Spark运行模式详解

Spark概述 Spark 可以在多种不同的运行模式下执行&#xff0c;每种模式都有其自身的特点和适用场景。 部署Spark集群大体上分为两种模式&#xff1a;单机模式与集群模式。大多数分布式框架都支持单机模式&#xff0c;方便开发者调试框架的运行环境。但是在生产环境中&#xff…

软件web化的趋势

引言 在信息技术飞速发展的今天&#xff0c;软件Web化已成为一个不可忽视的趋势。所谓软件Web化&#xff0c;即将传统的桌面应用软件转变为基于Web的应用程序&#xff0c;使用户能够通过浏览器进行访问和使用。传统软件通常需要在用户的计算机上进行安装和运行&#xff0c;而W…

Cadence OrCAD学习笔记(3)capture使用技巧_1

本期介绍capture的一些使用技巧。资料来源于小破站up主硬小二 1、导出像Visio规格的图纸 2、全局修改元件属性 然后保存、关闭即可。 3、导出BOM 4、导出网表 5、元件自动编号 6、capture软件和allegro关联 7、新建原理图symbol 以上为添加封装库的路径 如果要创建多部分的sy…

神器EasyRecovery2024中文电脑版下载!让数据恢复不再难

在数字化时代&#xff0c;数据就是我们的财富。无论是重要的工作报告&#xff0c;还是那些珍贵的生活瞬间照片&#xff0c;或是我们与朋友间的聊天记录&#xff0c;都储存在我们的电脑或手机中。然而&#xff0c;有时候&#xff0c;意外总是突如其来&#xff0c;电脑突然崩溃&a…

C++Qt操作Lotus Domino数据库 Lotus Domino C++连接Lotus Domino C++快速开发Lotus Domino

java连接domino C#连接domino python连接domino go连接domino,delphi连接domino Excel连接domino Flutter、微信小程序连接domino C 操作 Lotus Domino 数据库&#xff1a;自动化与效率的结合 引言 在企业级应用中&#xff0c;Lotus Domino 提供了一个强大的协作平台&#xff0…

【Linux】TCP协议【下一】{三次握手/四次挥手的深度解读==状态变化}

文章目录 本篇知识需要有TCP协议【中】的知识&#xff01;详情点击&#x1f447;1.测试一&#xff1a;服务器start函数不定义任何行为&#xff08;不调用accept&#xff09;的三次握手状态变化int listen(int sockfd, int backlog);的backlog参数全连接队列当全连接队列已满&am…

BGP策略实验(路径属性和选路规则)

要求&#xff1a; 1、使用preval策略&#xff0c;确保R4通过R2到达192.168.10.0/24 2、使用AS Path策略&#xff0c;确保R4通过R3到达192.168.11.0/24 3、配置MED策略&#xff0c;确保R4通过R3到达192.168.12.0/24 4、使用Local Preference策略&#xff0c;确保R1通过R2到达19…

Python轻松玩转excel操作指导

目录 一、一图概览 二、表格操作 三、内容操作 四、单元格操作 五、Pandas实现表格操作 六、常见场景示例 一、一图概览 ​ ​本文主要对openpyxl库的常用表格操作进行了梳理&#xff0c;熟练的运用后可极大地提升工作效率。 二、表格操作 #创建一个表格sheet.xlsx #…

C++编程揭秘:虚表机制与ABI兼容性的实例剖析

前言&#xff1a; 假设你的应用程序引用的一个库某天更新了&#xff0c;虽然 API 和调用方式基本没变&#xff0c;但你需要重新编译你的应用程序才能使用这个库&#xff0c;那么一般说这个库是源码兼容&#xff08;Source compatible&#xff09;&#xff1b;反之&#xff0c;如…

C语言指针相关知识(第五篇章)(非常详细版)

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、sizeof和strlen对比二、数组之间的比较&#xff08;依据strlen和sizeof来呈现&#xff09;&#xff08;一&#xff09;、一维整型数组&#xff08;二&#…

Value-Based Reinforcement Learning(2)

Temporal Difference &#xff08;TD&#xff09; Learning 上节已经提到了如果我们有DQN&#xff0c;那么agent就知道每一步动作如何做了&#xff0c;那么DQN如何训练那&#xff1f;这里面使用TD算法。 简略分析&#xff1a; 是的估计 是的估计 所以&#xff1a; Deep Re…

对vue3/core源码ref.ts文件API的认识过程

对toRef()API的认识的过程: 最开始认识toRef()是从vue3源码中的ref.ts看见的,右侧GPT已经举了例子 然后根据例子,在控制台输出ref对象是什么样子的: 这就是ref对象了,我们根据对象中有没有__v_isRef来判断是不是一个ref对象,当对象存在且__v_isRef true的时候他就判定为是一个…

Linux-组管理和权限管理

1 Liunx组的基本介绍&#xff1a; 在Linux中的每个用户必须属于一个组&#xff0c;不能独立于组外。在Linux中每个文件都有所有者、所在组、其他组的概念 所有者所在组其它组改变用户所在的组 2 文件/目录的所有者 一般文件的创建者&#xff0c;谁创建了该文件&#xff0c;就…

从程序被SQL注入来MyBatis 再谈 #{} 与 ${} 的区别

缘由 最近在的一个项目上面&#xff0c;发现有人在给我搞 SQL 注入&#xff0c;我真的想说我那么点资源测试用的阿里云服务器&#xff0c;个人估计哈&#xff0c;估计能抗住他的请求。狗头.png 系统上面的截图 数据库截图 说句实在的&#xff0c;看到这个之后我立马就是在…

游戏找不到d3dcompiler_43.dll怎么办,教你5种可靠的修复方法

在电脑使用过程中&#xff0c;我们经常会遇到一些错误提示&#xff0c;其中之一就是“找不到d3dcompiler43.dll”。这个问题通常出现在游戏或者图形处理软件中&#xff0c;它会导致程序无法正常运行。为了解决这个问题&#xff0c;我经过多次尝试和总结&#xff0c;找到了以下五…

idea2023的git从dev分支合并到主分支master

1.本地项目切换到主分支master 右键项目-git-Branches 依次点击项目-Remote-Origin-master-CheckOut 现在你的idea中的这个项目就是远程master分支的代码了。 2.合并dev分支到master 右击项目-git-Merge 选择origin-dev 点击Merge按钮&#xff0c;此时只是合并到本地的maste…

每日一题---有效的括号问题

文章目录 前言1.题目以及分析2.参考代码 前言 前面我们学习了栈的相关操作&#xff0c;现在我们做一道题&#xff0c;进行巩固 Leetcode—有效的括号 1.题目以及分析 这道题就可以使用栈进行操作&#xff0c;因为把最左边的括号当成栈底&#xff0c;最右边的是栈顶&#xff0c…

【每日刷题】Day49

【每日刷题】Day49 &#x1f955;个人主页&#xff1a;开敲&#x1f349; &#x1f525;所属专栏&#xff1a;每日刷题&#x1f34d; &#x1f33c;文章目录&#x1f33c; 1. 110. 平衡二叉树 - 力扣&#xff08;LeetCode&#xff09; 2. 501. 二叉搜索树中的众数 - 力扣&…