Showing 1 of 1 files from the diff.

@@ -93,7 +93,6 @@
Loading
93 93
    format: Format,
94 94
    exclude: HashSet<String>,
95 95
    exclude_regex: RegexSet,
96 -
    closure: Vec<fn(req: &ServiceRequest) -> String>,
97 96
}
98 97
99 98
impl Logger {
@@ -103,7 +102,6 @@
Loading
103 102
            format: Format::new(format),
104 103
            exclude: HashSet::new(),
105 104
            exclude_regex: RegexSet::empty(),
106 -
            closure: vec![],
107 105
        }))
108 106
    }
109 107
@@ -127,12 +125,21 @@
Loading
127 125
    }
128 126
129 127
    /// Register a closure to be run on the request output of the logger.
130 -
    /// Note: output from closure will be present at end of logger output. Multiple closure will be ran in the order they are registed.
131 -
    pub fn register_request_closure(
128 +
    /// Label passed into this function will be used to match output of given closure to label found in log format line with format %{label}xi.
129 +
    pub fn custom_request_replace(
132 130
        mut self,
131 +
        label: &'static str,
133 132
        closure: fn(req: &ServiceRequest) -> String,
134 133
    ) -> Self {
135 -
        Rc::get_mut(&mut self.0).unwrap().closure.push(closure);
134 +
        let inner = Rc::get_mut(&mut self.0).unwrap();
135 +
136 +
        for tf in inner.format.0.iter_mut() {
137 +
            if let FormatText::CustomLog(inner_label, _inner_closure) = tf {
138 +
                if inner_label == label {
139 +
                    *tf = FormatText::CustomLog(label.to_string(), Some(closure))
140 +
                };
141 +
            };
142 +
        }
136 143
        self
137 144
    }
138 145
}
@@ -148,7 +155,6 @@
Loading
148 155
            format: Format::default(),
149 156
            exclude: HashSet::new(),
150 157
            exclude_regex: RegexSet::empty(),
151 -
            closure: vec![],
152 158
        }))
153 159
    }
154 160
}
@@ -210,9 +216,6 @@
Loading
210 216
            for unit in &mut format.0 {
211 217
                unit.render_request(now, &req);
212 218
            }
213 -
            for closure in self.inner.closure.clone() {
214 -
                format.0.push(FormatText::render_closure(closure, &req));
215 -
            }
216 219
            LoggerResponse {
217 220
                fut: self.service.call(req),
218 221
                format: Some(format),
@@ -343,7 +346,8 @@
Loading
343 346
    /// Returns `None` if the format string syntax is incorrect.
344 347
    pub fn new(s: &str) -> Format {
345 348
        log::trace!("Access log format: {}", s);
346 -
        let fmt = Regex::new(r"%(\{([A-Za-z0-9\-_]+)\}([aioe])|[atPrUsbTD]?)").unwrap();
349 +
        let fmt =
350 +
            Regex::new(r"%(\{([A-Za-z0-9\-_]+)\}([aioe]|xi)|[atPrUsbTD]?)").unwrap();
347 351
348 352
        let mut idx = 0;
349 353
        let mut results = Vec::new();
@@ -371,6 +375,7 @@
Loading
371 375
                        HeaderName::try_from(key.as_str()).unwrap(),
372 376
                    ),
373 377
                    "e" => FormatText::EnvironHeader(key.as_str().to_owned()),
378 +
                    "xi" => FormatText::CustomLog(key.as_str().to_owned(), None),
374 379
                    _ => unreachable!(),
375 380
                })
376 381
            } else {
@@ -400,7 +405,7 @@
Loading
400 405
/// A string of text to be logged. This is either one of the data
401 406
/// fields supported by the `Logger`, or a custom `String`.
402 407
#[doc(hidden)]
403 -
#[derive(Debug, Clone)]
408 +
#[derive(Clone)]
404 409
pub enum FormatText {
405 410
    Str(String),
406 411
    Percent,
@@ -416,6 +421,7 @@
Loading
416 421
    RequestHeader(HeaderName),
417 422
    ResponseHeader(HeaderName),
418 423
    EnvironHeader(String),
424 +
    CustomLog(String, Option<fn(req: &ServiceRequest) -> String>),
419 425
}
420 426
421 427
impl FormatText {
@@ -471,13 +477,6 @@
Loading
471 477
        }
472 478
    }
473 479
474 -
    fn render_closure(
475 -
        closure: fn(req: &ServiceRequest) -> String,
476 -
        req: &ServiceRequest,
477 -
    ) -> FormatText {
478 -
        FormatText::Str(closure(req))
479 -
    }
480 -
481 480
    fn render_request(&mut self, now: OffsetDateTime, req: &ServiceRequest) {
482 481
        match *self {
483 482
            FormatText::RequestLine => {
@@ -531,6 +530,14 @@
Loading
531 530
                };
532 531
                *self = s;
533 532
            }
533 +
            FormatText::CustomLog(_, closure) => {
534 +
                let s = if let Some(closure) = closure {
535 +
                    FormatText::Str(closure(req))
536 +
                } else {
537 +
                    FormatText::Str("-".to_string())
538 +
                };
539 +
                *self = s;
540 +
            }
534 541
            _ => (),
535 542
        }
536 543
    }
@@ -724,7 +731,37 @@
Loading
724 731
    }
725 732
726 733
    #[actix_rt::test]
727 -
    async fn test_closure_logger_format() {
734 +
    async fn test_custom_closure_log() {
735 +
        let mut logger = Logger::new("%{CUSTOM}xi")
736 +
            .custom_request_replace("CUSTOM", |_req: &ServiceRequest| -> String {
737 +
                String::from("custom_log")
738 +
            });
739 +
        let mut format = Rc::get_mut(&mut logger.0).unwrap().format.0.clone();
740 +
741 +
        let req = TestRequest::default().to_srv_request();
742 +
743 +
        let now = OffsetDateTime::now_utc();
744 +
        for unit in &mut format {
745 +
            unit.render_request(now, &req);
746 +
        }
747 +
748 +
        let resp = HttpResponse::build(StatusCode::OK).force_close().finish();
749 +
        for unit in &mut format {
750 +
            unit.render_response(&resp);
751 +
        }
752 +
753 +
        let render = |fmt: &mut Formatter<'_>| {
754 +
            for unit in &format {
755 +
                unit.render(fmt, 1024, now)?;
756 +
            }
757 +
            Ok(())
758 +
        };
759 +
        let s = format!("{}", FormatDisplay(&render));
760 +
        assert!(s.contains(&now.format("custom_log")));
761 +
    }
762 +
763 +
    #[actix_rt::test]
764 +
    async fn test_closure_logger() {
728 765
        let srv = |req: ServiceRequest| {
729 766
            ok(req.into_response(
730 767
                HttpResponse::build(StatusCode::OK)
@@ -732,8 +769,8 @@
Loading
732 769
                    .finish(),
733 770
            ))
734 771
        };
735 -
        let logger = Logger::new("%% %{User-Agent}i %{X-Test}o %{HOME}e %D test")
736 -
            .register_request_closure(|_req: &ServiceRequest| -> String {
772 +
        let logger = Logger::new("%% %{User-Agent}i %{X-Test}o %{HOME}e %D %{CUSTOM}xi")
773 +
            .custom_request_replace("CUSTOM", |_req: &ServiceRequest| -> String {
737 774
                String::from("custom_log")
738 775
            });
739 776
Files Coverage
actix-files/src 0.00%
actix-http/src 37.77%
actix-multipart/src 0.00%
actix-web-actors/src 0.00%
awc/src 17.98%
src 87.14%
tests 100.00%
actix-web-codegen/src/route.rs 0.00%
Project Totals (126 files) 53.95%
1
ignore: # ignore codecoverage on following paths
2
  - "**/tests"
3
  - "test-server"
4
  - "**/benches"
5
  - "**/examples"
Sunburst
The inner-most circle is the entire project, moving away from the center are folders then, finally, a single file. The size and color of each slice is representing the number of statements and the coverage, respectively.
Icicle
The top section represents the entire project. Proceeding with folders and finally individual files. The size and color of each slice is representing the number of statements and the coverage, respectively.
Grid
Each block represents a single file in the project. The size and color of each block is represented by the number of statements and the coverage, respectively.
Loading