تسجيل حركة المرور الانتقائي لخدمات SOAP

في أي من مشاريعنا التي تتكامل مع خدمات العملاء ، يتم استخدام خدمات SOAP. وفي كل مشروع من هذا القبيل ، هناك مهمة لتسجيل المعلومات. نحن مهتمون بشكل خاص بطلبات تسجيل الدخول المتعلقة بالعمليات التجارية للحل. على سبيل المثال ، أدخل الموظف بيانات غير صحيحة وأرجعت الخدمة خطأ. نريد معرفة تفاصيل هذا الخطأ وتصحيحه في أسرع وقت ممكن أو إرساله للمناقشة مع العميل.

نود أن نرى طلبات عملائنا وإجابات الخدمة دون تغيير ، لأن هذا يساعد على حل المشاكل التي تنشأ.

في هذه المقالة ، سنعرض كيفية إعداد تسجيل حركة مرور انتقائي لخدمات SOAP.



المشكلة


بادئ ذي بدء ، يحتوي Spring على طلب مدمج وتسجيل استجابة ، والذي يتم تمكينه من خلال التكوين

logging.level.org.springframework.ws.client.MessageTracing.sent=TRACE logging.level.org.springframework.ws.client.MessageTracing.received=TRACE 

المشكلة هي أن هذه الإعدادات تشمل تسجيل جميع حركة مرور SOAP. نحن بحاجة فقط إلى عدد قليل من الأساليب ، ثم ليس بالكامل. على سبيل المثال ، لا نريد أن نرى طلبات تنزيل الملفات في السجلات ، لأن هذه كمية كبيرة من البيانات.

في Spring Framework ، يتمثل المعيار الفعلي لبناء صابون عميل في استخدام WebServiceGatewaySupport ، حيث يمكنك أيضًا إضافة معالجة الطلبات والاستجابة من خلال ClientInterceptor. لكن من الصعب في أساليبه فهم أي طريقة قام بها عميلنا بالاتصال. وليس من الواضح ما إذا كان من الضروري تسجيل طلب أو إجابة فقط أو كلها دفعة واحدة.

قرار


سنستخدم ClientInterceptor القياسي ، ولكن سنزوده بالمعلومات المفقودة حول توقيع الطريقة وعلامات "الإدخال" و "الإخراج" ، والتي يمكنك من خلالها تحديد ما إذا كان ينبغي تسجيل الطلب والاستجابة.

نحن نحتفظ على الفور بأن مثل هذا الحل لن يعمل مع تدفق التسلسل / إلغاء التسلسل. لكننا نستخدم Axiom ، التي لديها خيار "التخزين المؤقت للحمولة الصافية" ممكّن بشكل افتراضي ويعمل هذا الحل.

هيكل لتخزين المعلومات:

 import lombok.Data; @Data public class SoapLoggingInfo { private String method; private boolean input = false; private boolean output = false; } 

سياق لتخزين المعلومات:

 public class SoapLoggingContext { private static final ThreadLocal<SoapLoggingInfo> holder = new ThreadLocal<>(); public static void set(SoapLoggingInfo value) { holder.set(value); } public static SoapLoggingInfo get() { return holder.get(); } } 

لوضع المعلومات في سياق ما ، سوف نستخدم نهج AOP مع "شريحة" للطرق المشروحة واثنين من "النصائح": قبل وبعد هذه الطريقة تسمى.

شرح للطريقة:

 @Target({ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Component public @interface SoapLoggable { boolean value() default true; boolean input() default true; boolean output() default true; } 

تم وضع الجانب نفسه تحت القط.

جانب
 @Aspect @Component @Slf4j public class SoapLoggingAspect { @Value("${logging.soap.request.enabled:false}") private Boolean enabled; @Pointcut("execution(@ru.trueengineering.agentapi.logging.SoapLoggable * *(..))") public void soapLoggableMethod() {} @Before("soapLoggableMethod()") public void beforeSoapLoggable(JoinPoint joinPoint) { if (!enabled) { return; } SoapLoggable soapRequestLogger = getAnnotation(joinPoint); if (soapRequestLogger.value()) { SoapLoggingInfo loggingInfo = new SoapLoggingInfo(); loggingInfo.setInput(soapRequestLogger.input()); loggingInfo.setOutput(soapRequestLogger.output()); final Class<?> aClass = joinPoint.getTarget().getClass(); final Signature signature = joinPoint.getSignature(); final String name = signature.getName(); loggingInfo.setMethod(aClass.getSimpleName() + "." + name); SoapLoggingContext.set(loggingInfo); } } @After("soapLoggableMethod()") public void afterSoapLoggable(JoinPoint joinPoint) { SoapLoggingContext.set(null); } private SoapLoggable getAnnotation(JoinPoint joinPoint) { MethodSignature signature = (MethodSignature) joinPoint.getSignature(); Method method = signature.getMethod(); return method.getAnnotation(SoapLoggable.class); } } 


لنفصل


الشريحة عبارة عن تعبير يعني "جميع الطرق الموضحة بواسطة SoapLogable." نستخدم قدرات AspectJ:

 @Pointcut("execution(@ru.trueengineering.agentapi.logging.SoapLoggable * *(..))") public void soapLoggableMethod() {} 

هناك نصيحة تُدعى قبل طريقة سقطت تحت شريحة:

 @Before("soapLoggableMethod()") public void beforeSoapLoggable(JoinPoint joinPoint) {} 

في هذه الطريقة ، نأخذ تعليقًا توضيحيًا ، ونستخرج توقيع الأسلوب ومعلومات التعريف من التعليق التوضيحي ، ونشكل كائنًا لـ ClientInterceptor ونضعه في السياق.

هناك تلميح يسمى بعد استدعاء الطريقة التي سقطت تحت شريحة:

 @After("soapLoggableMethod()") public void afterSoapLoggable(JoinPoint joinPoint) {} 

انها مجرد مسح السياق. في الواقع ، قطع الأشجار تحت القط:

تسجيل
 @Component @Slf4j public class SoapLoggingInterceptor extends ClientInterceptorAdapter { @Override public boolean handleRequest(MessageContext messageContext) throws WebServiceClientException { SoapLoggingInfo info = SoapLoggingContext.get(); if (info != null && info.isInput()) { ByteArrayOutputStream xml = new ByteArrayOutputStream(); try { messageContext.getRequest().writeTo(xml); log.debug(": " + info.getMethod() + ", :" + xml.toString(StandardCharsets.UTF_8)); } catch (IOException e) { log.error("  SOAP request", e); } } return true; } @Override public boolean handleResponse(MessageContext messageContext) throws WebServiceClientException { return handleResponseOrFault(messageContext); } @Override public boolean handleFault(MessageContext messageContext) throws WebServiceClientException { return handleResponseOrFault(messageContext); } private boolean handleResponseOrFault(MessageContext messageContext) { SoapLoggingInfo info = SoapLoggingContext.get(); if (info != null && info.isOutput()) { ByteArrayOutputStream xml = new ByteArrayOutputStream(); try { messageContext.getResponse().writeTo(xml); log.debug(": " + info.getMethod() + ", :" + xml.toString(StandardCharsets.UTF_8)); } catch (IOException e) { log.error("  SOAP response", e); } } return true; } } 


نستخدم الطريقة القياسية "لاعتراض" ومعالجة طلبات SOAP ، لكننا نستخدم المعلومات من السياق لتسجيل انتقائي فقط للطرق التي نحتاجها.

الربح!


استخدام هذا النهج بسيط للغاية.

لا تريد أن ترى الطلب مع الملف المرفق؟ طيب!

 @SoapLoggable(input = false) public Optional<Osago2Response<ArrayOfKeyValuePairOfstringstring>> attachFile( final AttachFileRequest attachFileRequest) { return send(new WsAttachFileRequest(attachFileRequest)); } 

تريد أن ترى كل شيء؟ الأمر أسهل.

 @SoapLoggable public Optional<Osago2Response<CalcResult>> calculate(final CalcRequest calcRequest) { } 

استنتاج


شارك هذا المقال تجاربهم حول كيفية تكوين تسجيل حركة المرور الانتقائي لخدمات SOAP. بفضل هذا ، نراقب العمليات التجارية بسرعة ونستخدم السجلات في أي وقت لتحليل المشاكل. بالإضافة إلى ذلك ، يمكننا استخدام نفس الآلية لتتبع الوقت الذي يقضيه في تنفيذ طلب SOAP ولترجمة سبب الخطأ بسرعة.

نشارك أيضًا رابطًا مفيدًا لهذا الموضوع: مجموعة موجزة من الأمثلة على استخدام الشرائح والنصائح حول AspectJ .

Source: https://habr.com/ru/post/ar456712/


All Articles