// 加入收藏 加入收藏 function AddFavorite(sURL, sTitle) { try { window.external.addFavorite(sURL, sTitle); } catch (e) { try { window.sidebar.addPanel(sTitle, sURL, ""); } catch (e) { alert("加入收藏失败,请使用Ctrl+D进行添加"); } } } //设为首页 设为首页 function SetHome(obj, vrl) { try { obj.style.behavior = 'url(#default#homepage)'; obj.setHomePage(vrl); } catch (e) { if (window.netscape) { try { netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect"); } catch (e) { alert("此操作被浏览器拒绝!\n请在浏览器地址栏输入“about:config”并回车\n然后将 [signed.applets.codebase_principal_support]的值设置为'true',双击即可。"); } var prefs = Components.classes['@mozilla.org/preferences-service;1'].getService(Components.interfaces.nsIPrefBranch); prefs.setCharPref('browser.startup.homepage', vrl); } else { alert("您的浏览器不支持,请按照下面步骤操作:1.打开浏览器设置。2.点击设置网页。3.输入:" + vrl + "点击确定。"); } } }
  • 联系我们
  • CONTACT US
  • 电话:400-123-4567
  • 传真:+86-123-4567
  • Email:admin@youweb.com
  • 地址:广东省广州市天河区88号
  • 何如文雅地纪录操作日记?J9九游会官方网站
    浏览次数:

                                              这个操作日记的模板最终记实的实质是如许的款式:窜改了订单的配送员:从 “10090”,窜改到 “10099”,分明用户看到如许的操作日记是不睬睬的●。用户对待用户 ID 是 10090 照旧 10099 并不明晰,用户希冀看到的是:窜改了订单的配送员:从“张三()”●,窜改到“小明()”●。用户闭切的是配送员的姓名和电话。不过咱们步骤中转达的参数惟有配送员的 ID,没有配送员的姓名可电话。咱们可能通过上面的步骤,把用户的姓名和电话盘问出来,然后通过 LogRecordContext 达成。

                                              LogRecordContext 每实行一个步骤都邑压栈一个 Map◆◆,步骤实行完之后会 Pop 掉这个 Map◆●,从而避免变量共享和笼罩题目。

                                              借助 SLF4J 中的 MDC 器械类,把操作人放正在日记中,然后正在日记中联合打印出来。开始正在用户的中把用户的标识 Put 到 MDC 中。

                                              这时间可能看到,LogRecordContext 办理了操作日记模板上应用步骤参数以外变量的题目,同时避免了为了记实操作日记窜改步骤具名的安排。固然仍旧比之前的代码好了些,不过照旧需求正在交易代码内部加了一行交易逻辑无闭的代码,借使有“强迫症”的同砚还可能络续往下看,接下来咱们会讲授自界说函数的办理计划。下面再看另一个例子:

                                              这篇作品先容了操作日记的常睹写法,以及怎样让操作日记的达成尤其浅易、易懂;通过组件的四个模块,先容了组件的全部达成。对待上面的组件先容,群众借使有疑难,也接待正在文末留言,咱们会举行答疑。

                                              有了上面的理解◆●,仍旧得出一种咱们希冀的操作日记记实的方法,那么接下来看看怎样达成上面的逻辑。达成苛重分为下面几个办法:

                                              Spring 3 供应了一个出格壮健的性能:Spring EL,SpEL 正在 Spring 产物中是举动外达式求值的主题根底模块,它自己是可能摆脱 Spring 独立应用的。举个例子:

                                              通过 SpEL 外达式援用步骤上的参数,可能让变量填充到模板中到达动态的操作日记文本实质。 不过现正在另有几个题目需求办理:

                                              上面逻辑代码仍旧先容完毕,那么接下来需求把这些组件拼装起来,然后让用户去应用。正在应用这个组件的时间只需求正在 Springboot 的入口上增加一个说明 @EnableLogRecord(tenant = com.mzt.test)。个中 tenant 代外租户,是为了众租户应用的。

                                              下面是 IParseFunction 的接口界说:executeBefore函数代外了自界说函数是否正在交易代码实行之前解析◆◆,上面提到的盘问窜改之前的实质。

                                              操作日记普遍存正在于各个B端和少少C端体系中,好比:客服可能凭据工单的操作日记敏捷清晰哪些人对这个工单做了哪些操作J9九游会官方网站,进而敏捷地定位题目。操作日记和体系日记不相同●◆,操作日记一定要做到浅易易懂。因而怎样让操作日记不和交易逻辑耦合,怎样让操作日记的实质易于解析,让操作日记的接入尤其浅易◆?上面这些都是本文要回复的题目,苛重盘绕着怎样“温婉”地记实操作日记开展刻画。

                                              说明中除了上面提到参数外,还添加了 fail、category、detail、condition 等参数,这几个参数是为了知足特定的场景●,后面还会给出全部的例子。

                                              一提到动态模板●●,就会涉及到让变量通过占位符的方法解析模板◆,从而到达通过说明记实操作日记的方针记?J9九游会官方网站。模板解析的方法有许众种●,这里应用了 SpEL(Spring Expression Language,Spring外达式说话)来达成。咱们可能先写下希冀的记实日记的方法,然后再看下能否达成如许的性能。

                                              窜改后的代码正在说明上增加两个参数,一个是操作人,一个是操作日记需求绑定的对象。不过,正在普及的 Web 运用顶用户音讯都是留存正在一个线程上下文的静态步骤中,因而 operator 寻常是如许的写法(假定获取现在上岸用户的方法是 UserContext.getCurrentUser())。

                                              为了坚持浅易,组件的必填参数就两个。交易中的 AOP 逻辑大个人是应用 @Aspect 说明达成的,不过基于说明的 AOP 正在 Spring boot 1.5 中兼容性是有题目的,组件为了兼容 Spring boot1.5 的版本咱们手工达成 Spring 的 AOP 逻辑。

                                              正在产物仿单、客户权力须知方面,工行APP举行了适配◆,代销他行理财公司产物与代销工银理家产物均可能达成文字阅读,个人危害提示实质还用加粗、加大字体举行标注,客户阅读相对较为容易。

                                              交易可能达成这个留存接口,然后把日记留存正在任何存储介质上●。这里给了一个 2.2 节先容的通过留存正在日记文献中的例子,交易可能把留存创立成异步或者同步,可能和交易放正在一个事情中保障操作日记和交易的相似性◆●,也可能新斥地一个事情,保障日记的过错不影响交易的事情。交易可能留存正在 Elasticsearch何如文雅地纪录操作日记?J9九游会官方网站,、数据库或者文献中,用户可能凭据日记布局和日记的存储达成相应的盘问逻辑。

                                              美团研发质地及功效部 ◆,戮力于开发业界一流的继续交付平台,现聘请根底组件宗旨相干的工程师,坐标北京/上海。接待感兴会的同砚参与。可送达简历至:chao.(邮件大旨请解说:美团研发质地及功效部)。

                                              不过,“强迫症”是不希冀操作日记的代码嵌入正在交易逻辑中的●●。接下来,咱们思虑另一种达成方法:自界说函数。借使咱们可能通过自界说函数把用户 ID 转换为用户姓名和电话,那么就能办理这一题目,依照这个思绪,咱们把模板窜改为下面的样子:

                                              可能看到●●,操作日记的记实经久化是正在步骤实行完之后实行的,当步骤掷出特殊之后会先缉捕特殊,等操作日记经久化达成后再掷出特殊。正在交易的步骤实行之前,会对提前解析的自界说函数求值,办理了前面提到的需求盘问窜改之前的实质●。

                                              咱们可能正在说明的操作日记上记实固定文案,如许交易逻辑和交易代码可能做到解耦◆,让咱们的交易代码变得纯净起来●●。不妨有同砚注意到,上面的方法固然解耦了操作日记的代码,不过记实的文案并不切合咱们的预期,文案是静态的●◆,没有包蕴动态的文案◆,由于咱们需求记实的操作日记是: 用户%s窜改了订单的配送地点,从“%s”窜改到“%s”●。接下来,咱们先容一下怎样温婉地应用 AOP 天生动态的操作日记。

                                              通过摆设 Log 的摆设文献,把相闭操作日记的 Log 零丁放到一日记文献中。

                                              可能采用 LogUtil 的方法,也可能采用切面的方法天生日记模板,后续实质将会举行先容●◆。如许就可能把日记零丁留存正在一个文献中,然后通过日记采集可能把日记留存正在 Elasticsearch 或者数据库中,接下来看下怎样天生可读的操作日记。

                                              窜改类型的文本●,包蕴窜改前和窜改后的值,好比:2021-09-16 10:00 用户小明窜改了订单的配送地点:从“金灿灿小区”窜改到“银盏盏小区” ●,个中涉及变量配送的原地点“金灿灿小区”和新地点“银盏盏小区”。

                                              体系日记:体系日记苛重是为拓荒排查题目供应凭借何如文雅地纪录操作日,寻常打印正在日记文献中;体系日记的可读性哀求没那么高,日记中会包蕴代码的音讯,好比正在某个类的某一行打印了一个日记◆。

                                              Canal 是一款基于 MySQL 数据库增量日记解析,供应增量数据订阅和消费的开源组件,通过采用监听数据库 Binlog 的方法●◆,如许可能从底层清晰是哪些数据做了窜改,然后凭据更改的数据记实操作日记。

                                              这块逻辑苛重是一个,针对 @LogRecord 说明理解出需求记实的操作日记,然后把操作日记经久化,这里把说明定名为 @LogRecordAnnotation。接下来◆,咱们看下说明的界说:

                                              上面的操作日记苛重是通过一个 AOP 达成的,集体苛重分为 AOP 模块、日记解析模块、日记留存模块、Starter 模块;组件供应了4个扩展点●,判袂是:自界说函数、默认统治人、交易留存和盘问;交易可能凭据自身的交易个性定制切合自身交易的逻辑◆。

                                              要么和产物司理 PK 一下,让产物司理把文案从“窜改了订单的配送地点:从 xx 窜改到 yy” 改为 “窜改了订单的配送地点为:yy”。不过从用户体验上来看,第一种文案更人性化少少,分明咱们不会 PK 凯旋的◆●。那么咱们就一定要把这个 oldAddress 盘问出来然后供操作日记应用了。另有一种办理设施是:把这个参数放到操作日记的线程上下文中,供说明上的模板应用●。咱们依照这个思绪再改下操作日记的实摩登码。

                                              为明晰决上面题目●◆,寻常采用 AOP 的方法记实日记,让操作日记和交易逻辑解耦,接下来看一个浅易的 AOP 日记的例子。

                                              上面看起来题目并不大◆,正在窜改地点的交易逻辑步骤中应用一行代码记实了操作日记◆●,接下来再看一个更杂乱的例子:

                                              组件正在解析 operator 的时间,就决断说明上的 operator 是否是空,借使说明上没有指定,咱们就从 IOperatorGetService 的 getUser 步骤获取了。借使都获取不到,就会报错。

                                              下面的例子把变量放到了 LogRecordContext 中,然后 SpEL 外达式就可能成功的解析步骤上不存正在的参数了,通过上面的 SpEL 的例子可能看出◆,要把步骤的参数和 LogRecordContext 中的变量都放到 SpEL 的getValue步骤的 Object 中才可能成功的解析外达式的值。下面看下怎样达成:

                                              浅易的动态的文本记实,好比:2021-09-16 10:00 订单创筑,订单号:NO.11089999,个中涉及变量订单号“NO.11089999”。

                                              看起来没有什么题目,不过应用 LogRecordAnnotation 的步骤内部嵌套了另一个应用 LogRecordAnnotation 步骤的时间,流程就酿成下面的样子:

                                              下面是 LogRecordContext 的达成,这个类内部通过一个 ThreadLocal 变量坚持了一个栈,栈内部是个 Map,Map 对应了变量的名称和变量的值。

                                              操作日记:苛重是对某个对象举行新增操作或者窜改操作跋文实下这个新增或者窜改,操作日记哀求可读性对比强,由于它苛重是给用户看的,好比订单的物流音讯,用户需求清晰正在什么光阴爆发了什么事件。再好比,客服对工单的统治记灌音讯。

                                              如许就不需求正在 modifyAddress 步骤中通过 LogRecordContext.putVariable() 创立老的疾递员了●,通过直接新加一个自界说函数 queryOldUser() 参数把派送订单转达进去◆◆,就能查到之前的配送人了,只需求让步骤的解析正在 modifyAddress() 步骤实行之前运转◆。如许的话,咱们让交易代码又变得纯净了起来,同时也让“强迫症”不再感觉难受了。

                                              这种方法的好处是和交易逻辑全部散开。短处也很明明,控制性太高,只可针对数据库的更改做操作日记记实,借使窜改涉及到其他团队的 RPC 的移用●◆,就没设施监听数据库了,举个例子:给用户发送知照,知照任事寻常都是公司内部的民众组件,这时间只可正在移用 RPC 的时间手工记实发送知照的操作日记了。

                                              可能看到上面的例子应用了两个步骤代码,外加一个 getLogContent 的函数达成了操作日记的记实。当交易变得杂乱后◆,记实操作日记放正在交易代码中会导致交易的逻辑对比繁杂,最终导致 LogUtils.logRecord() 步骤的移用存正在于许众交易的代码中,况且犹如 getLogContent() 如许的步骤也散落正在各个交易类中,对待代码的可读性和可庇护性来说是一个灾难。下面先容下怎样避免这个灾难。

                                              可能看到◆◆,当步骤二实行了开释变量后,络续实行步骤一的 logRecord 逻辑,此时解析的时间 ThreadLocalMapString, Object的 Map 仍旧被开释掉,因而步骤一就获取不到对应的变量了。步骤一和步骤二共用一个变量 Map 另有个题目是:借使步骤二创立了和步骤一相通的变量两个步骤的变量就会被互相笼罩。因而最终 LogRecordContext 的变量的性命周期需假使下面的样子:

                                              接下来,咱们需求办理第三个题目:为了记实交易操作记实增加了一个 oldAddress 变量,不管奈何样这都不是一个好的达成方法,因而接下来,咱们需求把 oldAddress 变量从窜改地点的步骤具名上去掉●。不过操作日记确实需求 oldAddress 变量,奈何办呢◆?

                                              个中 deliveryUser 是自界说函数,应用大括号把 Spring 的 SpEL 外达式包裹起来,如许做的好处:一是把 SpEL(Spring Expression Language,Spring外达式说话)和自界说函数分辨开便于解析;二是借使模板中不需求 SpEL 外达式解析可能容易的识别出来●,省略 SpEL 的解析提升本能。这时间咱们创造上面代码还可能优化成下面的样子:

                                              如许的话,每个 @LogRecord 的说明上的操作人都是这么长一串。为了避免过众的反复代码,咱们可能把说明上的 operator 参数创立为非必填,如许用户可能填写操作人。不过●,借使用户不填写咱们就取 UserContext 的 user(下文会先容怎样取 user )。最终,最浅易的日记酿成了下面的样子:

                                            在线咨询
                                            电话咨询
                                            400-123-4567
                                            $(function () { $("#tid3").toggleClass('act'); ; }) if (!window.jQuery) { document.write(unescape("%3Cscript src='/public/static/common/js/jquery.min.js' type='text/javascript'%3E%3C/script%3E")); document.write(unescape("%3Cscript type='text/javascript'%3E try{jQuery.noConflict();}catch(e){} %3C/script%3E")); } if (window.jQuery) { (function($){ default_switch(); //简体繁体互换 function default_switch() { var home_lang = getCookie('home_lang'); if (home_lang == '') { home_lang = 'cn'; } if ($.inArray(home_lang, ['zh','cn'])) { var obj = $('#jquerys2t_1573822909'); var isSimplified = getCookie('jquerys2t_1573822909'); if ('cn' == isSimplified) { $('body').t2s(); $(obj).text('繁體'); } else if ('zh' == isSimplified) { $('body').s2t(); $(obj).text('简体'); } } } //简体繁体互换 $('#jquerys2t_1573822909').click(function(){ var obj = this; var isSimplified = getCookie('jquerys2t_1573822909'); if ('' == isSimplified || 'cn' == isSimplified) { $('body').s2t(); // 简体转繁体 setCookie('jquerys2t_1573822909', 'zh'); $(obj).text('简体'); } else { $('body').t2s(); // 繁体转简体 setCookie('jquerys2t_1573822909', 'cn'); $(obj).text('繁體'); } }); })(jQuery); }