前端错误日志上报解决方案

news2024/11/13 10:08:03

前言

项目上线之后,用户如果出现错误(代码报错、资源加载失败以及其他情况),基本上没有办法复现,如果用户出了问题但是不反馈或直接不用了,对开发者或公司来说都是损失。
由于我这个项目比较小,只是一个迷你商城,所以不需要收集很复杂的数据,只需要知道有没有资源加载失败、哪行代码报错就可以了,市面上有很多现成的监控平台比如sentry,在这里我选择通过nodejs自己搭一个服务。

概述

我的项目是使用Vue2写的,所以本文主要是讲Vue相关的部署过程
1、部署后台服务(使用express)
2、收集前端错误(主要是Vue)
3、提交信息到后台分析源码位置及记录日志

js异常处理

function test1 () {
    console.log('test1 Start');
    console.log(a);
    console.log('test1 End');
}

function test2 () {
    console.log('test2 Start');
    console.log('test2 End');
}

test1();
test2();

在这里插入图片描述
这里可以看到,当js运行报错后,代码就不往下执行了,这是因为js是单线程,具体可以看看事件循环,这里不做解释

接下来看看使用异步的方式执行,可以看到没有影响代码的继续运行

function test1 () {
    console.log('test1 Start');
    console.log(a);
    console.log('test1 End')
}

function test2 () {
    console.log('test2 Start');
    console.log('test2 End')
}

setTimeout(() => {
    test1();
}, 0)

setTimeout(() => {
    test2();
}, 0)

在这里插入图片描述
那报错之后我们如何收集错误呢?

try catch

function test1 () {
    console.log('test1 Start');
    console.log(a);
    console.log('test1 End')
}

try {
  test1();
} catch (e) {
  console.log(e);
}

在这里插入图片描述
使用try catch将代码包裹起来之后,当运行报错时,会将收集到的错误传到catch的形参中,打印之后我们可以拿到错误信息和错误的堆栈信息,但是try catch无法捕获到异步的错误

function test1 () {
    console.log('test1 Start');
    console.log(a);
    console.log('test1 End')
}

try {
  setTimeout(function() {
    test1();
  }, 100);
} catch (e) {
  console.log(e);
}

在这里插入图片描述
可以看到try catch是无法捕获到异步错误的,这时候就要用到window的error事件

监听error事件

window.addEventListener('error', args => {
  console.log(args);
  return true;
}, true)

function test1 () {
    console.log('test1 Start');
    console.log(a);
    console.log('test1 End')
}

setTimeout(function() {
  test1();
}, 100);

在这里插入图片描述
除了window.addEventListener可以监听error之后,window.onerror也可以监听error,但是window.onerrorwindow.addEventListener相比,无法监听网络异常

window.addEventListener

<img src="https://www.baidu.com/abcdefg.gif">
<script>
  window.addEventListener('error', args => {
    console.log(args);
    return true;
  }, true) // 捕获
</script>

在这里插入图片描述

window.onerror

<img src="https://www.baidu.com/abcdefg.gif">
<script>
  window.onerror = function(...args) {
    console.log(args);
  }
</script>

由于无法监听到,这里就不放图了

unhandledrejection

到目前为止,Promise已经成为了开发者的标配,加上新特性引入了async await,解决了回调地狱的问题,但window.onerrorwindow.addEventListener,对Promise报错都是无法捕获

window.addEventListener('error', error => {
  console.log('window', error);
})
new Promise((resolve, reject) => {
  console.log(a);
}).catch(error => {
  console.log('catch', error);
})

在这里插入图片描述
可以看到,监听window上的error事件是没有用的,可以每一个Promise写一个catch,如果觉得麻烦,那么就要使用一个新的事件,unhandledrejection

window.addEventListener('unhandledrejection', error => {
  console.log('window', error);
})
new Promise((resolve, reject) => {
  console.log(a);
})

在这里插入图片描述
其中,reason中存放着错误相关信息,reason.message是错误信息,reason.stack是错误堆栈信息

Promise错误也可以使用 try catch捕获到,这里就不做演示了

至此,js中同步、异步、资源加载、Promise、async/await都有相对应的捕获方式

window.addEventListener('unhandledrejection', error => {
  console.log('window', error);
  throw error.reason;
})

window.addEventListener('error', error => {
  console.log(error);
  return true;
}, true)

vue异常处理

由于我的项目使用Vue2搭建的,所以还需要处理一下vue的报错

export default {
  name: 'App',
  mounted() {
    console.log(aaa);
  }
}

现在的项目基本上都是工程化的,通过工程化工具打包出来的代码长这样,上面的代码打包后运行
在这里插入图片描述
在这里插入图片描述
通过报错提示的js文件,查看后都是压缩混淆之后的js代码,这时候就需要打包时生成的source map文件了,这个文件中保存着打包后代码和源码对应的位置,我们只需要拿到报错的堆栈信息,通过转换,就能通过source map找到对应我们源码的文件及出错的代码行列信息
那我们怎么才能监听error事件呢?
使用Vue的全局错误处理函数Vue.config.errorHandler
src/main.js中写入以下代码

Vue.config.errorHandler = (err, vm, info) => {
  console.log('Error: ', err);
  console.log('vm', vm);
  console.log('info: ', info);
}

现在打包vue项目

打包vue之后然后通过端口访问index.html,不建议你双击打开,如果你没改过打包相关的东西,双击打开是不行的,可以通过vs code装插件live server,然后将打包文件夹通过vs code打开
在这里插入图片描述

上报错误数据

经过上述的异常处理后,我们需要将收集到的错误进行整理,将需要的信息发送到后台,我这里选择使用ajax发请求到后端,当然你也可以使用创建一个图片标签,将需要发送的数据拼接到src上
这里我选择使用tracekit库来解析错误的堆栈信息,axios发请求,dayjs格式化时间

npm i tracekit
npm i axios
npm i dayjs

安装完成后在src/main.js中引入tracekit、axios、dayjs

上报Vue错误

import TraceKit from 'tracekit';
import axios from 'axios';
import dayjs from 'dayjs';

const protcol = window.location.protocol;
let errorMonitorUrl = `${protcol}//127.0.0.1:9999`;
const errorMonitorVueInterFace = 'reportVueError'; // vue错误上报接口
TraceKit.report.subscribe((error) => {
  const { message, stack } = error || {};

  const obj = {
    message,
    stack: {
      column: stack[0].column,
      line: stack[0].line,
      func: stack[0].func,
      url: stack[0].url
    }
  };

  axios({
    method: 'POST',
    url: `${errorMonitorUrl}/${errorMonitorVueInterFace}`,
    data: {
      error: obj,
      data: {
        errTime: dayjs().format('YYYY-MM-DD HH:mm:ss'),
        isMobile: /iPhone|iPad|iPod|Android/i.test(navigator.userAgent), // 是否移动端
        isWechat: /MicroMessenger/i.test(navigator.userAgent), // 是否微信浏览器
        isIOS: /iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream, // 两个都是false就是未知设备
        isAndroid: /Android/.test(navigator.userAgent) && !/Windows Phone/.test(navigator.userAgent)
      },
      browserInfo: {
        userAgent: navigator.userAgent,
        protcol: protcol
      }
    }
  }).then(() => {
    console.log('错误上报成功');
  }).catch(() => {
    console.log('错误上报失败');
  });
});

Vue.config.errorHandler = (err, vm, info) => {
  TraceKit.report(err);
}

如果你还需要其他的数据就自己加
打包vue之后然后通过端口访问index.html,不建议你双击打开,如果你没改过打包相关的东西,双击打开是不行的,可以通过vs code装插件live server,然后将打包文件夹通过vs code打开
现在去项目中看看发出去的请求参数是什么
可以看到我们需要的数据都已经收集到了,上报失败是肯定的,因为我们还没有写好接口
在这里插入图片描述

上报window错误

接下来在监听window的error事件,也向后台发送一个错误上报请求

const errorMonitorWindowInterFace = 'reportWindowError'; // window错误上报接口
window.addEventListener('error', args => {
  const err = args.target.src || args.target.href;
  const obj = {
    message: '加载异常' + err
  };
  if (!err) {
    return true;
  }
  axios({
    method: 'POST',
    url: `${errorMonitorUrl}/${errorMonitorWindowInterFace}`,
    data: {
      error: obj,
      data: {
        errTime: dayjs().format('YYYY-MM-DD HH:mm:ss'),
        isMobile: /iPhone|iPad|iPod|Android/i.test(navigator.userAgent), // 是否移动端
        isWechat: /MicroMessenger/i.test(navigator.userAgent), // 是否微信浏览器
        isIOS: /iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream, // 两个都是false就是未知设备
        isAndroid: /Android/.test(navigator.userAgent) && !/Windows Phone/.test(navigator.userAgent)
      },
      browserInfo: {
        userAgent: navigator.userAgent,
        protcol: protcol
      }
    }
  }).then(() => {
    console.log('错误上报成功');
  }).catch(() => {
    console.log('错误上报失败');
  });
  return true;
}, true);

搭建监控后台

创建一个文件夹,名字随便,然后在终端中打开文件夹,初始化npm

npm init -y

初始化完成后创建一个server.js,这里我使用express进行搭建后端,source-map用于解析js.map文件,这些库后面会用到

npm i express
npm i nodemon
npm i source-map

下好包之后在server.js中输入以下代码,然后在终端输入nodemon server.js

const express = require('express');
const path = require('path')const fs = require('fs');

const PORT = 9999;

const app = express();
app.use(express.urlencoded({ extended: true }));
app.use(express.json());

app.get('/', (req, res) => {
  res.send('Hello World!').status(200);
})

app.listen(PORT, () => {
  console.log(`服务启动成功,端口号为:${PORT}`)
})

服务启动之后,访问本地的9999端口,查看是否生效,当看到屏幕上显示Hello World!表示我们的后端服务成功跑起来了,接下来就是写错误的上传接口
在这里我将为Vue和Window监控分别写一个接口(因为我懒得一个接口做判断区分,如果你觉得两个接口太麻烦,那你也可以自己优化成一个接口)

编写Vue错误上报接口

server.js中继续添加

const SourceMap = require('source-map');

app.post('/reportVueError',async (req, res) => {
  const urlParams = req.body;
  console.log(`收到Vue错误报告`);
  console.log('urlParams', urlParams);

  const stack = urlParams.error.stack;
  // 获取文件名
  const fileName = path.basename(stack.url);
  // 查找map文件
  const filePath = path.join(__dirname, 'uploads', fileName + '.map');
  const readFile = function (filePath) {
    return new Promise((resolve, reject) => {
      fs.readFile(filePath, { encoding: 'utf-8'}, (err, data) => {
        if (err) {
          console.log('readFileErr', err)
          return reject(err);
        }
        resolve(JSON.parse(data));
      })
    })
  }

  async function searchSource({ filePath, line, column }) {
    const rawSourceMap = await readFile(filePath);
    const consumer = await new SourceMap.SourceMapConsumer(rawSourceMap);
    const res = consumer.originalPositionFor({ line, column })

    consumer.destroy();
    return res;
  }

  let sourceMapParseResult = '';
  try {
    // 解析sourceMap结果
    sourceMapParseResult = await searchSource({ filePath, line: stack.line, column: stack.column });
  } catch (err) {
    sourceMapParseResult = err;
  }
  console.log('解析结果', sourceMapParseResult)
  res.send({
    data: '错误上报成功',
    status: 200,
  }).status(200);
})

然后nodemon会自动重启服务,如果你不是用nodemon启动的,那自己手动重启一下
打包vue之后然后通过端口访问index.html,不建议你双击打开,如果你没改过打包相关的东西,双击打开是不行的,可以通过vs code装插件live server,然后将打包文件夹通过vs code打开,通过live server运行,此时应该会报跨域问题
在这里插入图片描述

设置允许跨域

可以自己手动设置响应头实现跨域,我这里选择使用cors库

npm i cors
const cors = require('cors');
app.use(cors()); // 这条需要放在 const app = express(); 后

此时重新运行后台,再观察
在这里插入图片描述
在这里插入图片描述
此时发现,解析map文件报错了,那是因为我们还没有上传map文件
server.js同级目录下创建一个uploads文件夹
回到打包vue打包文件目录dist,将js文件夹中所有js.map结尾的文件剪切到创建的文件夹中,如果你打包文件中没有js.map,那是因为你没有打开生成js.map的开关,打开vue.config.js,在defineConfig中设置属性productionSourceMaptrue,然后重新打包就可以了

module.exports = defineConfig({
  productionSourceMap: true, // 设置为true,然后重新打包
  transpileDependencies: true,
  lintOnSave: false,
  configureWebpack: {
    devServer: {
      client: false
    }
  }
})

为什么是剪切?如果真正的项目上线时,你把js.map文件上传了,别人拿到之后是可以知道你的源码的,所以必须剪切,或者复制之后回到dist目录删掉所有js.map

在这里插入图片描述
这时候我们再刷新网页,然后看后台的输出,显示src/App.vue的第10行有错
在这里插入图片描述
在这里插入图片描述
编写window错误上传接口

// 处理Window报错
app.post('/reportWindowError',async (req, res) => {
  const urlParams = req.body;
  console.log(`收到Window错误报告`);
  console.log('urlParams', urlParams);

  res.send({
    data: '错误上报成功',
    status: 200,
  }).status(200);
})

此时我们去vue项目中添加一个img标签,获取一张不存在的图片即可出发错误,由于不用解析,所以这里就不再上传js.map
在这里插入图片描述
在这里插入图片描述

写入日志

错误上报之后我们需要记录下来,接下来我们改造一下接口,收到报错之后写一下日志

我需要知道哪一天的日志报错了,所有我在node项目中也下载dayjs用来格式化时间.

npm i dayjs

此处的日志记录内容只是我自己需要的格式,如果你需要其他格式请自己另外添加

vue错误写入日志

// let sourceMapParseResult = '';
// try {
//  // 解析sourceMap结果
//  sourceMapParseResult = await searchSource({ filePath, line: stack.line, column: //stack.column });
//} catch (err) {
//  sourceMapParseResult = err;
//}
//console.log('解析结果', sourceMapParseResult)

// 直接将下面的内容粘贴在上面的log下面

const today = dayjs().format('YYYY-MM-DD') // 今天

const logDirPath = path.join(__dirname, 'log');
const logFilePath = path.resolve(__dirname, 'log/' + `log-${today}.txt`)

if (!fs.existsSync(logDirPath)) {
  console.log(`创建log文件夹`)
  fs.mkdirSync(logDirPath, { recursive: true });
}
if (!fs.existsSync(logFilePath)) {
  console.log(`创建${today}日志文件`)
  fs.writeFileSync(logFilePath, '', 'utf8');
}

const writeStream = fs.createWriteStream(logFilePath, { flags: 'a' });
writeStream.on('open', () => {
  // writeStream.write('UUID:' + urlParams.data.uuid + '\n');
  writeStream.write('错误类型:Window' + '\n');
  writeStream.write('错误发生时间:' + urlParams.data.errTime + '\n');
  writeStream.write('IP:' + req.ip + '\n');
  writeStream.write(`安卓: ${urlParams.data.isAndroid} IOS: ${urlParams.data.isIOS} 移动端: ${urlParams.data.isMobile} 微信: ${urlParams.data.isWechat} (安卓和ios同时为false表示未知设备)` + '\n');
  writeStream.write('用户代理:' + urlParams.browserInfo.userAgent + '\n');
  writeStream.write('错误信息:' + urlParams.error.message + '\n');
  writeStream.write('---------------------------------- \n');

  writeStream.end(() => {
    console.log('vue错误日志写入成功');
    console.log('---------------------');
    res.send({
      data: '错误上报成功',
      status: 200,
    }).status(200);
  });
})

writeStream.on('error', err => {
  res.send({
    data: '错误上报失败',
    status: 404,
  }).status(404);
  console.error('发生错误:', err);
})

window错误写入日志

和vue写入的方式差不多,存在优化空间

const today = dayjs().format('YYYY-MM-DD') // 今天

const logDirPath = path.join(__dirname, 'log');
const logFilePath = path.join(__dirname, 'log' + `/log-${today}.txt`)

if (!fs.existsSync(logDirPath)) {
  console.log(`创建log文件夹`)
  fs.mkdirSync(logDirPath, { recursive: true });
}
if (!fs.existsSync(logFilePath)) {
  console.log(`创建${today}日志文件`)
  fs.writeFileSync(logFilePath, '', 'utf8');
}

const writeStream = fs.createWriteStream(logFilePath, { flags: 'a' });
writeStream.on('open', () => {
  writeStream.write('错误类型:Window' + '\n');
  writeStream.write('错误发生时间:' + urlParams.data.errTime + '\n');
  writeStream.write('IP:' + req.ip + '\n');
  writeStream.write(`安卓: ${urlParams.data.isAndroid} IOS: ${urlParams.data.isIOS} 移动端: ${urlParams.data.isMobile} 微信: ${urlParams.data.isWechat} (安卓和ios同时为false表示未知设备)` + '\n');
  writeStream.write('用户代理:' + urlParams.browserInfo.userAgent + '\n');
  writeStream.write('错误信息:' + urlParams.error.message + '\n');
  writeStream.write('---------------------------------- \n');

  writeStream.end(() => {
    console.log('window错误日志写入成功');
    console.log('---------------------');
    res.send({
      data: '错误上报成功',
      status: 200,
    }).status(200);
  });
})

writeStream.on('error', err => {
  res.send({
    data: '错误上报失败',
    status: 404,
  }).status(404);
  console.error('发生错误:', err);
})

在这里插入图片描述
在这里插入图片描述
至此,收集错误,上报错误,写入日志已经全部完成。

其他

错误监控持久化运行在服务器

这个可以使用pm2,在服务器上使用node全局安装pm2

pm2 ls #显示所有pm2启动的应用
pm2 start /xxx/xxx # 启动/xxx/xxx应用
pm2 save # 保存当前应用列表
pm2 stop id # id 通过pm2 ls查看
pm2 logs id # 查看日志

自动上传js.map文件

如果每次打包后都手动复制js.map文件的到uploads文件夹下,似乎有些麻烦
虽然麻烦,但是我自己还是没有自动上传,原因是如果打包就自动上传,那么如果项目还未发布,但是文件已经替换掉之前的文件了,新版本未发布之前,vue的错误就无法解析了,当然,如果你每次上传都不删除以前的文件也是可以的

修改vue项目

在vue项目src下创建一个plugin目录,新建一个UploadSourceMap.js,将下面的代码粘贴进去

const glob = require('glob')
const path = require('path')
const http = require('http')
const fs = require('fs')

class UploadSourceMap {
  constructor (options) {
    this.options = options
  }

  apply (compiler) {
    console.log('UploadSourceMap')

    // 在打包完成后运行
    compiler.hooks.done.tap('UploadSourceMap', async stats => {
      const list = glob.sync(path.join(stats.compilation.outputOptions.path, '**/*.js.map'))
      for (const item of list) {
        const fileName = path.basename(item);
        console.log(`开始上传${fileName}`)
        await this.upload(this.options.url, item)
        console.log(`上传${fileName}完成`)
      }
    })
  }

  upload (url, file) {
    return new Promise((resolve, reject) => {
      const req = http.request(
        `${url}/upload?name=${path.basename(file)}`,
        {
          method: 'POST',
          headers: {
            'Content-Type': 'application/octet-stream',
            Connection: 'keep-alive',
            'Transfer-Encoding': 'chunked'
          }
        }
      )
      fs.createReadStream(file)
        .on('data', chunk => {
          req.write(chunk)
        })
        .on('end', () => {
          req.end()
          // 删除文件
          fs.unlink(file, (err) => {
            if (err) {
              console.error(err)
            }
          })
          resolve()
        })
    })
  }
}

module.exports = UploadSourceMap

修改vue.config.js

主要是引入UploadSourceMap,并且在configureWebpack => plugins下使用

const { defineConfig } = require('@vue/cli-service')
const UploadSourceMap = require('./src/plugin/UploadSourceMap')

module.exports = defineConfig({
  productionSourceMap: true,
  transpileDependencies: true,
  lintOnSave: false,
  configureWebpack: {
    plugins: [
      new UploadSourceMap({
        url: 'http://127.0.0.1:9999' // 后面换成自己的服务器地址
      })
    ]
  }
})

在这里插入图片描述

修改后台

修改server.js,新增一个上传文件的接口

app.post('/upload', (req, res) => {
  const fileName = req.query.name
  const filePath = path.join(__dirname, 'uploads', fileName)

  if (!fs.existsSync(path.dirname(filePath))) {
    fs.mkdirSync(path.dirname(filePath), { recursive: true })
  }

  const writeStream = fs.createWriteStream(filePath)

  req.on('data', (chunk) => {
    writeStream.write(chunk)
  })

  req.on('end', () => {
    writeStream.end(() => {
      res.status(200).send(`File ${fileName} has been saved.`)
    })
  })

  writeStream.on('error', (err) => {
    fs.unlink(filePath, () => {
      console.error(`Error writing file ${fileName}: ${err}`)
      // res.status(500).send(`Error writing file ${fileName}.`)
    })
  })
})

然后现在重新打包,观察打包输出
在这里插入图片描述

最后

尽量是不要开启跨域,否则谁都能给发请求到后台,如果要开跨域,那需要做好判断,主域名不符合的直接返回404终止这次请求。

市面上的监控有很多,有些甚至能实现录制用户操作生成gif,本文只是实现一个基本的错误监控,如有错误请指出。
源码参考:https://github.com/ytanck/demos/tree/master/error-monitor-demo

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

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

相关文章

AI绘画工具介绍:以新奇角度分析与探索AI绘画艺术与技术的交汇点

目录 前言 一、AI绘画工具的前沿技术 1.1 深度学习的进化 1.2 GANs的创新应用 1.3 风格迁移的多样化 1.4 交互式AI绘画的智能化 二、艺术与技术的交汇点 2.1 艺术创作的普及化 2.2 艺术风格的创新 2.3 艺术与科技的深度融合 三、新颖的思考角度 3.1 AI作为艺术创作…

nginx平滑重启和php-fpm平滑重启

https://www.jianshu.com/p/c7809490979ahttp://xn--nginxphp-fpm-dc3k7692b4eb248gffzdlr6cx05cfuhyucca 1.在php-fpm.conf的配置中增加配置&#xff0c;让php-fpm重启前如果存在已经在处理的请求&#xff0c;先处理完再重启&#xff1a; 2.重启命令执行前&#xff0c;先…

【Python】简单爆破破解

暴力破解是一种针对密码的破译方法&#xff0c;通过逐个推算或使用工具批量验证来找到正确的密码。暴力破解是信息安全领域中一个非常重要的话题。在当今数字化时代&#xff0c;个人信息、企业数据和各种网络服务都依赖于密码来保护安全。因此&#xff0c;破解密码成为黑客获取…

熵权法模型(评价类问题)

一. 概念 利用信息熵计算各个指标的权重&#xff0c;从而为多指标的评价类问题提供依据。 指标的变异程度越小&#xff0c;所反映的信息量也越少&#xff0c;所以其对应的权值也应该越低。 指标的变异程度&#xff08;或称为变异性、波动性&#xff09;&#xff1a;描述了一…

AI智能名片S2B2C商城小程序在社群团购平台产品供应链优化中的应用探索

摘要&#xff1a;在社群团购这一新兴零售模式迅速崛起的背景下&#xff0c;产品供应链的效率和稳定性成为制约其进一步发展的关键因素。特别是在社群团购行业尚处于初步发展阶段的今天&#xff0c;产品资源的稀缺性尤为突出。针对这一问题&#xff0c;本文深入探讨了AI智能名片…

计算机网络408考研 2021

2021 计算机网络408考研2021年真题解析_哔哩哔哩_bilibili 1 1 11 1 1 11

飞睿智能8km无人机WiFi图传模块,高清、稳定、超远距!实时传输新高度

在数字化飞速发展的今天&#xff0c;无人机已经从一个遥不可及的科幻概念&#xff0c;变成了我们日常生活中的得力助手。无论是航拍美景、农业植保&#xff0c;还是紧急救援、物流配送&#xff0c;无人机都展现出了其独特的优势。而在这背后&#xff0c;一个至关重要的技术支撑…

VUE学习-day20

VUE学习-day20 1. 下载与安装 1.1 安装Node.js Node.js是一个免费、开源、跨平台的 JavaScript 运行时环境&#xff0c;它让开发人员能够创建服务器、Web 应用、命令行工具和脚本。 我们可以通过node.js来下载vue的组件和创建vue项目 下载安装教程:https://blog.csdn.net/…

检索增强生成 (RAG)在实践中的挑战

检索增强生成 (RAG) 应用程序已成为大型语言模型 (LLM) 领域的强大工具&#xff0c;但在从原型过渡到生产环境时&#xff0c;经常面临挑战。 RAG 模型在需要深度知识整合和情境理解的应用中尤其有效&#xff0c;例如法律研究、科学文献综述和复杂的客户服务查询。检索和生成过…

mysql 中的共享锁与排他锁

mysql 中的共享锁与排他锁 1、共享锁&#xff08;Shared Lock&#xff09;2、排他锁&#xff08;Exclusive Lock&#xff09; &#x1f496;The Begin&#x1f496;点点关注&#xff0c;收藏不迷路&#x1f496; 在MySQL的InnoDB存储引擎中&#xff0c;锁是管理并发访问数据的关…

Leetcode—328. 奇偶链表【中等】

2024每日刷题&#xff08;153&#xff09; Leetcode—328. 奇偶链表 实现代码 /*** Definition for singly-linked list.* struct ListNode {* int val;* ListNode *next;* ListNode() : val(0), next(nullptr) {}* ListNode(int x) : val(x), next(nullptr)…

入门岛3-Git 的使用与项目构建

任务概览 任务1: 破冰活动&#xff1a;自我介绍 任务2: 实践项目&#xff1a;构建个人项目 参考&#xff1a;git流程&#xff1a; csdn1 csdn2 任务1: 破冰活动&#xff1a;自我介绍 1.Git 是一种开源的分布式版本控制系统&#xff0c;广泛应用于软件开发领域&#xff0c;尤…

DPDI Online在线kettle调度工具

1. DPDI简介 DPDI Online 是一款基于Kettle的强大在线任务调度平台&#xff0c;凭借其高效与灵活性&#xff0c;专为调度和监控Kettle客户端生成的ETL任务而设计 2. DPDI使用 2.1 DPDI登录 2.2 DPDI核心功能 2.2.1 工作台 DPDI online首页主要是显示任务监控信息&#xff…

k8s 四种Service类型(ClusterIP、NodePort、LoadBalancer、ExternalName)详解

&#x1f407;明明跟你说过&#xff1a;个人主页 &#x1f3c5;个人专栏&#xff1a;《Kubernetes航线图&#xff1a;从船长到K8s掌舵者》 &#x1f3c5; &#x1f516;行路有良友&#xff0c;便是天堂&#x1f516; 目录 一、引言 1、k8s概述 2、Service在Kubernetes中的…

细说盘点10种自动化拣货技术

导语 大家好&#xff0c;我是社长&#xff0c;老K。专注分享智能制造和智能仓储物流等内容。 在现代物流和仓储管理中&#xff0c;拣货技术的选择对于提高效率和降低成本至关重要。 本文将为您介绍10种流行的拣货技术&#xff0c;并分享它们的参数和应用场景&#xff0c;以帮助…

使用本地大模型从论文PDF中提取结构化信息

1 安装ollama 点击前往网站 https://ollama.com/ &#xff0c;下载ollama软件&#xff0c;支持win、Mac、linux 2 下载LLM ollama软件目前支持多种大模型&#xff0c; 如阿里的&#xff08;qwen、qwen2&#xff09;、meta的(llama3、llama3.1)&#xff0c; 读者根据自己电脑…

《加密与解密》读书笔记1

1、常用断点包括INT3断点、硬件断点、内存断点和消息断点。 1.1 INT断点&#xff1a;一个常用的断点&#xff0c;在OD和x64dbg中按F2快捷键来设置/取消断点。当执行一个INT3断点时&#xff0c;该地址处的内容就被调试器使用INT3指令替换掉了&#xff0c;此时OD和x64dbg将INT3隐…

flink1.18 编译遇到的问题

1. flink-runtime-web编译失败 源码编译时一直卡在 [INFO] Running ‘npm ci --cache-max0 --no-save’ in 处理方法&#xff1a; 修改flink-runtime-web/pom.xml文件 将<arguments>ci --cache-max0 --no-save ${npm.proxy}</arguments> 替换为&#xff1a;<a…

Delphi5实现鱼C记事本程序(TRichEdit版)

效果图 鱼C记事本程序 程序图标 Delphi5的程序图标需要ICO后缀名的文件&#xff0c;已经上传到CSDN&#xff0c;可以用一下。 有什么快捷生成ICO文件的方法&#xff0c;可以在评论区分享一下噢&#xff0c;谢谢&#xff01;&#xff01; MainMenu菜单组件 TRichEdit 组件 在…

多线程(2)——线程的六种状态

1. 线程的所有状态 进程状态&#xff1a; 就绪&#xff1a;正在 cpu 上执行&#xff0c;或者随时可以去 cpu 上执行 阻塞&#xff1a;暂时不能参与 cpu 执行 Java 的线程&#xff0c;对于状态做了更详细的区分&#xff0c;不仅仅是就绪和阻塞了&#xff0c;六种&#xff1a…