ããã«ã¯FïŒã®ããããªãããšãããã«èšããªããã°ãªããªãã®ã§ãCïŒã§èšè¿°ããŸããFïŒã«ç²ŸéããŠããªãããCïŒã«ç²ŸéããŠãã人ã«ã¯ãMicrosoftã®ææ°èšäºããå§ãããŸãã
èªããšãã«WTFãå°ãªããªãã®ãå©ããŸãã ç§ã®èšäºã¯æ§æã®ãã¥ãŒããªã¢ã«ã§ã¯ãããŸãããã¿ã¹ã¯ã³ã³ããã¹ã
Akka.NETã§èšè¿°ããããµãŒãã¹ããããããŸããŸãªããã¹ããã°ã«å€§éã®æ
å ±ããã³ãããŸãã éçšéšéã¯ãããã®ãã°ã«ãã°ãä»ããregexpã§ãããããã°ã«èšé²ããŠããšã©ãŒã®æ°ïŒããžãã¹ã§ã¯ãªãïŒããµãŒãã¹ã«å«ãŸããã¡ãã»ãŒãžã®æ°ãããã³çºä¿¡ã¡ãã»ãŒãžã®æ°ã«ã€ããŠåŠç¿ããŸãã ããã«ããã®æ
å ±ã¯ElasticDBãInfluxDBã«æ³šãããGrafanaãšKibanaã§ç°ãªãã¹ã©ã€ã¹ãšéèšã§è¡šç€ºãããŸãã
è€éã«èãããŸããã1æ¥ã«æ°åGBã®ããã¹ãã¬ããŒãžãçæãããµãŒãã¹ã®ããã¹ããã°ã解æããããšã¯æ©æµã®ãªãäœæ¥ã§ãã ãã®ãããã¿ã¹ã¯ãçºçããŸãã-ãµãŒãã¹ã¯ãšã³ããã€ã³ããäžããããšãã§ããã¯ãã§ãããšã³ããã€ã³ãããã«ããŠãããã«é¢ãããã¹ãŠã®æ
å ±ãäžåºŠã«ååŸã§ããŸãã
次ã®ããã«åé¡ã解決ããŸãã
- ã¡ããªã¯ã¹ã®ãã¡ã€ã³ã¢ãã«ãæžããŸããã
- ã¡ããªãã¯ã®ãã¡ã€ã³ã¢ãã«ãApp.Metricsã®å®è£
ã«ãããã³ã°ããCookieããã¯ã¹ãäžããŸã
- æ§é åãã¡ã€ã³ãã¬ãŒãäœæããŸããããããã¯ãå
éšã®Akkaãã¬ãŒã䜿çšããŸã
- æ©èœçãªã¢ã¯ã¿ãŒã®ã©ãããŒãäœæããŸããããããã«ãããã¡ããªãã¯ãšãã¬ãŒã䜿çšããŠäœæ¥ãé ãããŸãã
- ãã¹ãŠããŸãšããŠå®è¡ãã
ã¡ããªãã¯ã®ãã¡ã€ã³ã¢ãã«
App.Metricsã«ã¯6çš®é¡ã®ãã¥ãŒããããŸãã
- ã«ãŠã³ã¿ãŒ
- Apdex
- ã²ãŒãž
- ãã¹ãã°ã©ã
- ã¡ãŒãã«
- ã¿ã€ããŒ
æåã®ã€ãã¬ãŒã·ã§ã³ã§ã¯ãã«ãŠã³ã¿ãŒãã¿ã€ããŒã...ã¡ãŒã¿ãŒã§ååã§ã:)
æåã«ãã¿ã€ããšã€ã³ã¿ãŒãã§ãŒã¹ã«ã€ããŠèª¬æããŸãïŒãã¹ãŠãæäŸããããã§ã¯ãããŸããããªããžããªã®æåŸã®ãªã³ã¯ãåç
§ããŠãã ããïŒã
ãŸããã¡ããªãã¯ã«é¢ãããã¹ãŠã®ã¡ãã»ãŒãžã¯ãEventStreamïŒAkka.Netèªäœã®ã¡ãã»ãŒãžãã¹ïŒãä»ããŠç¹å¥ãªã¢ã¯ã¿ãŒïŒåŸã§å®çŸ©ããŸãïŒã«éãããããšã«åæããŸãã
ããšãã°ããªããžã§ã¯ãã®ç¹å®ã®æéã枬å®ã§ããã¯ãã®ã¿ã€ããŒïŒ
type IMetricsTimer = abstract member Measure : Amount -> unit abstract member Measure : Amount * Item -> unit
ãŸãã¯ãæ°éã®æ瀺ãããå Žåãšãªãå Žåã®äž¡æ¹ã§å¢æžã§ããã«ãŠã³ã¿ïŒ
type IMetricsCounter = abstract member Decrement : unit -> unit abstract member Decrement : Amount -> unit abstract member Decrement : Amount * Item -> unit abstract member Increment : unit -> unit abstract member Increment : Amount -> unit abstract member Increment : Amount * Item -> unit
ãã¹çšã®ã³ãã³ãã®äŸïŒ
type DecrementCounterCommand = { CounterId : CounterId DecrementAmount : Amount Item : Item } type CreateCounterCommand = { CounterId : CounterId Context : ContextName Name : MetricName MeasurementUnit : MeasurementUnit ReportItemPercentages : bool ReportSetItems : bool ResetOnReporting : bool }
æãéèŠãªããšã¯ããã¹ãééããå¯èœæ§ã®ããã¡ãã»ãŒãžãšãã¡ããªãã¯ã¢ã¯ã¿ãŒãå¿çããã¡ãã»ãŒãžã決å®ããããšã§ãã ãããè¡ãã«ã¯ãå·®å¥çµåã䜿çšããŸãã
type MetricsMessage = | DecrementCounter of DecrementCounterCommand | IncrementCounter of IncrementCounterCommand | MarkMeter of MarkMeterCommand | MeasureTime of MeasureTimeCommand | CreateCounter of CreateCounterCommand | CreateMeter of CreateMeterCommand | CreateTimer of CreateTimerCommand
ããã§ãã€ã³ã¿ãŒãã§ã€ã¹ãå®è£
ããå¿
èŠãããããããæåã®æ®µèœã®çµããã§ãã æ©èœçãªã¹ã¿ã€ã«ã§å®è£
ããŸãã æ©èœãéããŠã
ã¡ãŒã¿ãŒã®äœæäŸïŒ
let private createMeter (evtStream: EventStream) meterId = { new IMetricsMeter with member this.Mark amount = this.Mark (amount, Item None) member this.Mark item = this.Mark (Amount 1L, item) member this.Mark (amount, item) = evtStream.Publish <| MarkMeter { MeterId = meterId; Amount = amount; Item = item }
CïŒã®äžçã®äººã
ã®ããã«ãç§ã¯ã¢ããã°ãäžããŸãïŒ
private IMetricsMeter createMeter(EventStream evtStream, MeterId meterId) { private class TempClass : IMetricsMeter { public void Mark(long amount) { Mark(amount, ""); } public void Mark(string item) { Mark(1, item); } public void Mark(long amount, string item) { evtStream.Publish(new MarkMeter {...});
ã¢ããã°ãã³ã³ãã€ã«ãããªãããšãæ°ã«ããªãã§ãã ãããããã¯æ£åžžã§ã ã¡ãœããã®æ¬äœã«ãããã©ã€ããŒãã¯ã©ã¹ã¯ãã³ã³ãã€ã©ã«ãã£ãŠæ··ä¹±ããŸãã ããããFïŒã§ã¯ãã€ã³ã¿ãŒãã§ã€ã¹ãä»ããŠå¿åã¯ã©ã¹ãè¿ãããšãã§ããŸãã
泚æãã¹ãäž»ãªããšã¯ãã¡ãŒã¿ãŒã移åããå¿
èŠããããšããã¡ãã»ãŒãžããã¹ã«æããããšã§ããããã¯ãMeterIdã«ãã£ãŠæ±ºå®ãããŸãã
IMetricsAdapterã§ãåãããšãè¡ããŸããã 圌ã«ã¯å€ãã®æ¹æ³ããããŸãã
member this.CreateMeter (name, measureUnit, rateUnit) = let cmd = { MeterId = MeterId (toId name) Context = context Name = name MeasurementUnit = measureUnit RateUnit = rateUnit } evtStream.Publish <| CreateMeter cmd createMeter evtStream cmd.MeterId
ã¿ã€ããŒã®äœæãèŠæ±ãããšãäœæã¡ãã»ãŒãžããã¹ã«éä¿¡ããevtStreamãšcmd.MeterIdã䜿çšããŠcreateMeterã¡ãœããã®çµæãåŒã³åºãå
ã«è¿ããŸãã
ãã®çµæãäžèšã®ããã«-IMetricsMeterã
ãã®åŸãActorSystemã®æ¡åŒµãäœæããŠãã©ãããã§ãIMetricsAdapterãåŒã³åºãããšãã§ããããã«ããŸãã
type IActorContext with member x.GetMetricsProducer context = createAdapter x.System.EventStream context
ã¡ããªãã¯ãšããªãŒãã±ãŒã¹ã®ã¢ã¯ã¿ãŒ
次ã®2ã€ã®ã¢ã¯ã¿ãŒãå¿
èŠã§ãã
- 1ã€ç®ã¯ããã¹å
ã§MetricsMessageã®ååšããªãã¹ã³ããã¡ããªãã¯ãäœæ/æžã蟌ã¿ãŸãã
- 2çªç®ã®ã¢ã¯ã¿ãŒã¯1ã€ã®ã¡ãœããã§WebApiãä¿æããGETèŠæ±ã§åéããããã¹ãŠã®æ
å ±ãéä¿¡ããŸãã
ApiControllerãããã«å®çŸããŸããç°¡åã§ãã
type public MetricController(metrics: IMetrics) = inherit ApiController() [<HttpGet>] [<Route("metrics")>] member __.GetMetrics() = __.Ok(metrics.Snapshot.Get())
次ã«ãEventStreamãããã¹ãŠã®MetricsMessageãèªã¿åãããããã§äœããå®è¡ããã¢ã¯ã¿ãŒé¢æ°ã宣èšããŸãã åŒæ°ã䜿çšããŠé¢æ°ã«IMetricsäŸåé¢ä¿ãå®è£
ããŸããå
éšã§ã¯ãéåžžã®èŸæžã䜿çšããŠãã¹ãŠã®ã¡ããªãã¯ã®ãã£ãã·ã¥ãäœæããŸãã
ãªãConcurrentDictionaryã§ã¯ãªãã®ã§ããïŒ ãããŠãã¢ã¯ã¿ãŒã¯ã¡ãã»ãŒãžãé çªã«åŠçããããã§ãã ã¢ã¯ã¿ãŒå
ã®ç«¶åç¶æ
ããã£ããããã«ã¯ãæå³çã«èªåã®è¶³ãæã€å¿
èŠããããŸãã
let createRecorder (metrics: IMetrics) (mailbox: Actor<_>) = let self = mailbox.Self let counters = new Dictionary<CounterId, ICounter>() let meters = new Dictionary<MeterId, IMeter>() let timers = new Dictionary<TimerId, ITimer * TimeUnit>() // ... let handle = function | DecrementCounter evt -> match counters.TryGetValue evt.CounterId with | (false, _) -> () | (true, c) -> let (Amount am) = evt.DecrementAmount match evt.Item with | Item (Some i) -> c.Decrement (i, am) | Item None -> c.Decrement (am) | CreateMeter cmd -> match meters.TryGetValue cmd.MeterId with | (false, _) -> let (ContextName ctxName) = cmd.Context let (MetricName name) = cmd.Name let options = new MeterOptions( Context = ctxName, MeasurementUnit = toUnit cmd.MeasurementUnit, Name = name, RateUnit = toTimeUnit cmd.RateUnit) let m = metrics.Provider.Meter.Instance options meters.Add(cmd.MeterId, m) | _ -> () // match subscribe typedefof<MetricsMessage> self mailbox.Context.System.EventStream |> ignore let rec loop() = actor { let! msg = mailbox.Receive() handle msg return! loop() } loop()
ç°¡åãªæå³-圌ãã¯ç°ãªãã¡ããªãã¯ã®èŸæžã®åœ¢ã§å
éšç¶æ
ãçºè¡šããã¡ãã»ãŒãžåŠçé¢æ°MetricsMessageãçºè¡šããMetricsMessageã«ãµãã¹ã¯ã©ã€ãããã¡ãŒã«ããã¯ã¹ããååž°ã¡ãã»ãŒãžåŠçé¢æ°ãè¿ããŸããã
ã¡ããªãã¯ãæäœããããã®ã¡ãã»ãŒãžã¯ã次ã®ããã«åŠçãããŸãã
- ã©ã®ã¡ãã»ãŒãžã確èªããŸãïŒäžèŽãã¿ãŒã³ã䜿çšïŒ
- ãã®IDãæã€ãã®ãã£ã¯ã·ã§ããªã®ã¡ããªãã¯ãæ¢ããŠããŸãïŒãã®ãããFã§TryGetValueãè¿ãã«ããã«ïŒboolãobjïŒã«çŸãããã¿ãŒã³ããããŸãïŒ
- ãããã¡ããªãã¯ã®äœæèŠæ±ã§ãããããã«ãªãå Žå-äœæãèŸæžã«è¿œå
- ãããã¡ããªãã¯ã䜿çšãããªã¯ãšã¹ãã§ãããããã§ããå Žåã䜿çšããŸã
ãŸããã³ã³ãããŒã©ãŒãé«ãOwinãã¹ããäžããã¢ã¯ã¿ãŒãå¿
èŠã§ãã
ãããè¡ãããã«ãconfigããã³IDependencyResolverã®åœ¢åŒã§äŸåé¢ä¿ãåãé¢æ°ãäœæããŸãã éå§æã«å€±æããªãããã«ãã¢ã¯ã¿ãŒã¯èªåã«ã¡ãã»ãŒãžãéä¿¡ããå€ãAPIã®å¯èœãªDisposeïŒïŒãšæ°ããAPIã®äœæãéå§ããŸãã ãããŠåã³ããªããªã ããèªäœã®äžã®ã¢ã¯ã¿ãŒã¯åæçã§ãããããå¯å€ç¶æ
ã䜿çšã§ããŸãã
type IMetricApiConfig = abstract member Host: string abstract member Port: int type ApiMessage = ReStartApiMessage let createReader (config: IMetricApiConfig) resolver (mailbox: Actor<_>) = let startUp (app: IAppBuilder) = let httpConfig = new HttpConfiguration(DependencyResolver = resolver) httpConfig.Formatters.JsonFormatter.SerializerSettings.Converters.Add(new MetricDataConverter()) httpConfig.Formatters.JsonFormatter.Indent <- true httpConfig.MapHttpAttributeRoutes() httpConfig.EnsureInitialized() app.UseWebApi(httpConfig) |> ignore let uri = sprintf "http://%s:%d" config.Host config.Port let mutable api = {new IDisposable with member this.Dispose() = ()} let handleMsg (ReStartApiMessage) = api.Dispose() api <- WebApp.Start(uri, startUp) mailbox.Defer api.Dispose mailbox.Self <! ReStartApiMessage let rec loop() = actor { let! msg = mailbox.Receive() handleMsg msg return! loop() } loop()
ãŸããmailbox.Deferã䜿çšããŠã¢ã¯ã¿ãŒãæçµçã«åæ¢ãããšãã«ãapi.Disposeã¡ãœãããé
延ã¿ã¹ã¯ã«ã¹ããŒããŸãã ãŸããapiå€æ°ã®åæç¶æ
ã«ã¯ããªããžã§ã¯ãåŒãä»ããŠã¹ã¿ãã䜿çšããŸããããã«ããã空ã®IDisposableãªããžã§ã¯ããäœæãããŸãã
æ§é åãã¬ãŒã®äœæ
ã¿ã¹ã¯ã®æå³ã¯ãAkka.Netãããã¬ãŒã®ã©ãããŒãäœæããããšã§ãïŒILoggingAdapterã€ã³ã¿ãŒãã§ã€ã¹ãä»ããŠè¡šç€ºãããŸãïŒãããã䜿çšããŠãæäœã®æéãšæ
å ±ã®å
¥åããªããïŒéŒ»ç·ã ãã§ãªããåå¥ã®ããžãã¹ã±ãŒã¹ïŒã枬å®ã§ããŸãã
ãã¬ãŒã®ãã¹ãŠã®å
¥åã¯ã1ã€ã®çµåã«å²ãŸããŠããŸãã
type Fragment = | OperationName of string | OperationDuration of TimeSpan | TotalDuration of TimeSpan | ReceivedOn of DateTimeOffset | MessageType of Type | Exception of exn
ãã¬ãŒèªäœã¯ãã®ã€ã³ã¿ãŒãã§ãŒã¹ã§åäœããŸãïŒ
type ILogBuilder = abstract OnOperationBegin: unit -> unit abstract OnOperationCompleted: unit -> unit abstract Set: LogLevel -> unit abstract Set: Fragment -> unit abstract Fail: exn -> unit abstract Supress: unit -> unit abstract TryGet: Fragment -> Fragment option
éåžžã®ã¯ã©ã¹ãä»ããŠäœæããŸãã
type LogBuilder(logger: ILoggingAdapter) = let logFragments = new Dictionary<System.Type, Fragment>() let stopwatch = new Stopwatch() let mutable logLevel = LogLevel.DebugLevel interface ILogBuilder with
ãããããéåžžã®èŸæžãå¿
èŠãªã®ã¯ãªãã§ããããïŒ åè¿°ã®ããã«ããã®LogBuilderã¯ãåäžã®æäœãåŠçãããšãã«ã¢ã¯ã¿ãŒå
ã§äœ¿çšããããšãç®çãšããŠããŸãã 競äºåã®ããããŒã¿æ§é ã䜿çšããŠãæå³ããããŸããã
ã€ã³ã¿ãŒãã§ãŒã¹ã®å®è£
æ¹æ³ã®äŸã瀺ããŸãã
let set fragment = logFragments.[fragment.GetType()] <- fragment member x.OnOperationBegin() = stopwatch.Start() member this.Fail e = logLevel <- LogLevel.ErrorLevel set <| Exception e member this.OnOperationCompleted() = stopwatch.Stop() set <| OperationDuration stopwatch.Elapsed match tryGet <| ReceivedOn DateTimeOffset.MinValue with | Some (ReceivedOn date) -> set <| TotalDuration (DateTimeOffset.UtcNow - date) | _ -> () match status with | Active -> match (logLevel) with | LogLevel.DebugLevel -> logger.Debug(message()) | LogLevel.InfoLevel -> logger.Info(message()) | LogLevel.WarningLevel -> logger.Warning(message()) | LogLevel.ErrorLevel -> logger.Error(message()) | x -> failwith(sprintf "Log level %s is not supported" <| string x) | Supressed -> ()
æãèå³æ·±ãã®ã¯ãOnOperationCompletedïŒïŒã®ããžãã¯ã§ãã
- ã¿ã€ããŒãåæ¢ããOperationDurationãã©ã°ã¡ã³ããä»ããŠãã¬ãŒã«çµéæéãæžã蟌ã¿ãŸã
- ãã°ã«ReceivedOnãã©ã°ã¡ã³ããããå ŽåïŒç§ã®ã¢ãã«ã§ã¯ãã¡ãã»ãŒãžããµãŒãã¹ã«å°éããæéãæå³ããŸãïŒãTotalDurationãä»ããŠã¡ãã»ãŒãžããµãŒãã¹ã§è²»ãããåèšæéããã°ã«æžã蟌ã¿ãŸãã
- ãã¬ãŒããªãã«ãªã£ãŠããªãå ŽåïŒSupressïŒïŒã¡ãœããã䜿çšïŒãã¡ãã»ãŒãžïŒïŒã¡ãœããã䜿çšããŠAkkaã«æ
å ±ãæžã蟌ã¿ãŸããããã®ã¡ãœããã¯æã£ãŠããŸããããã¡ãã»ãŒãžã¿ã€ããèæ
®ããŠãã¹ãŠã®ãã©ã°ã¡ã³ããæååã«åéããŸã
æ©èœçãªã¢ã¯ã¿ãŒã®ã©ãããŒãäœæãã
æãéæ³ã®éšåãããã«ãããå®å
šãªãã®ã³ã°ãå®è¡ããå®åæãªãã§ãåçŽãªã¡ãã»ãŒãžåŠçé¢æ°ãäœæã§ããŸãã
äœãéæãããã§ããïŒ ãŸããèªçŽãããïŒ
- ç§ãã¡ãããããš ç§ãã¡ã®å Žåãããã¯æäœã®ååã§ãã æ©èœçãªã¢ã¯ã¿ãŒã«ã¯ã1ã€ã®ã¿ã€ãã®FuncActorããããŸã
- åŠçãããã¡ãã»ãŒãžã®ã¿ã€ã
- ãã®ãããªæ©èœïŒæ¬è³ªçã«ã¢ã¯ã¿ãŒïŒãã·ã¹ãã ã«ããã€ååšããã
- æäœãå®äºããã®ã«ããã£ãæéã衚瀺ãã
- ãµãŒãã¹ã®å
¥ãå£ã§ãã®ã¡ãã»ãŒãžãåä¿¡ããŠââããçµéããåèšæéã衚瀺ããŸã
- ç¹å¥ãªæ¹æ³ã§çºçããå Žåã¯ãšã©ãŒãèšé²ãã
- äžèšã®ãã¹ãŠãèæ
®ããããšãªããåçŽãªã¡ãã»ãŒãžåŠçé¢æ°ãäœæã§ãã
äžèšã®ãã¹ãŠãè¡ãã«ã¯ãLinq.Expressionsã圹ç«ã¡ãŸãã FïŒã®QuotationExpressionsã§ãããè¡ãæ¹æ³ã¯ããããŸãããtkã ããããã³ã³ãã€ã«ããç°¡åãªæ¹æ³ãèŠã€ãããŸããã§ããã 誰ãããªãã·ã§ã³ãæäŸããŠããããå¬ããã§ãã
ãããã£ãŠãæåã«ããã«ããŒã¿ã€ãã®ãã¢ãš1ã€ã®ã¡ãœããã宣èšããŸãã
type Expr<'T,'TLog when 'TLog :> ILogBuilder> = Expression<System.Action<Actor<'T>, 'T, 'TLog>> type Wrap = static member Handler(e: Expression<System.Action<Actor<'T>, 'T, #ILogBuilder>>) = e let toExprName (expr: Expr<_,_>) = match expr.Body with | :? MethodCallExpression as methodCall -> methodCall.Method.Name | x -> x.ToString()
Exprã¯ãã¡ãŒã«ããã¯ã¹ããã®ã¢ã¯ã·ã§ã³ïŒåãçæãããèªåèªèº«ãŸãã¯åãäžè¬çã«åæ¢ããå¿
èŠãããå ŽåïŒãåŠçäžã®ã¡ãã»ãŒãžãããã³ãã¬ãŒïŒç¹å¥ãªã¢ã¯ã·ã§ã³ãå®è¡ããå¿
èŠãããå ŽåïŒãå«ãåŒã§ãã
Wrap.HandlerïŒExprïŒ-ãfun mb msg log->ïŒïŒããªã©ã®éåžžã®FïŒåŒãããã«æžã蟌ã¿ãåºåã§Linq.ExpressionsãååŸã§ããŸãã
toExprNameã¯ãåŒãã¡ãœããåŒã³åºãïŒMethodCallExpressionïŒã§ããå ŽåããŸãã¯åã«åŒãæååã«ãã£ã¹ãããããšããŠããå Žåã«ãã¡ãœããã®ååãååŸããã¡ãœããã§ãã
ãfun mb msg log-> handleMsg msgãã®ãããªåŒã®å ŽåãtoExprNameã¯ãhandleMsgããè¿ããŸãã
次ã«ãã©ãããŒãäœæããŠæ©èœçãªã¢ã¯ã¿ãŒãäœæããŸãã åºåã®éå§ã¯æ¬¡ã®ããã«ãªããŸãã
let loggerActor<'TMsg> (handler: Expr<'TMsg,_>) (mailbox: Actor<'TMsg>) = let exprName = handler |> toExprName let metrics = mailbox.Context.GetMetricsProducer (ContextName exprName) let logger = mailbox.Log.Value
次ã®ããã«ãå
¥åã«ãã³ãã©ãŒã®ã¿ãéä¿¡ããŸãã ã¡ãŒã«ããã¯ã¹ã¯AkkaèªäœïŒéšåã¢ããªã±ãŒã·ã§ã³ïŒã«ãã£ãŠã¹ããŒãããŸãã
äœæããActorSystemã®æ¡åŒµæ©èœã䜿çšããŠãå€ã¡ããªãã¯ã§IMetricsAdapterã®ã€ã³ã¹ã¿ã³ã¹ãååŸããŸãã ãŸãããã¬ãŒå€ã§Akkaãã¬ãŒãååŸããŸãã
次ã«ããã®ã¢ã¯ã¿ãŒã«å¿
èŠãªãã¹ãŠã®ã¡ããªãã¯ãäœæããããã«äœ¿çšããŸãã
let errorMeter = metrics.CreateMeter (MetricName "Error Rate", Errors) let instanceCounter = metrics.CreateCounter (MetricName "Instances Counter", Items) let messagesMeter = metrics.CreateMeter (MetricName "Message Processing Rate", Items) let operationsTimer = metrics.CreateTimer (MetricName "Operation Durations", Requests, MilliSeconds, MilliSeconds) instanceCounter.Increment() mailbox.Defer instanceCounter.Decrement
ã芧ã®ãšãããinstanceCounterã®å€ãå¢ãããã¢ã¯ã¿ãŒã®åæ¢æã«ãã®ã«ãŠã³ã¿ãŒã®æžå°ãèšå®ããŸãã
ãã¬ãŒã§ç¥ã£ãŠãããã©ã¡ãŒã¿ãŒãå
¥åããå¿
èŠãªã¡ããªãã¯ãååŸããã¡ãœãããããã«å¿
èŠã§ãã
ãã®ã³ãŒãã§ã¯ãæäœã®ååããã¬ãŒã«ã¹ããŒãããã°ã®çµãããåŒã³åºããæäœæéãã¿ã€ããŒã¡ããªãã¯ã«ããããããã¡ãã»ãŒãžã¿ã€ããã¡ãã»ãŒãžã¡ããªãã¯ã«ããããããŸãã
let completeOperation (msgType: Type) (logger: #ILogBuilder) = logger.Set (OperationName exprName) logger.OnOperationCompleted() match logger.TryGet(OperationDuration TimeSpan.Zero) with | Some(OperationDuration dur) -> operationsTimer.Measure(Amount (int64 dur.TotalMilliseconds), Item (Some exprName)) | _ -> () messagesMeter.Mark(Item (Some msgType.Name))
ã¢ã¯ã¿ãŒå
ã®äŸå€ãåŠçããã«ã¯ã次ã®ã¡ãœããã圹ç«ã¡ãŸãã
let registerExn (msgType: Type) e (logger: #ILogBuilder) = errorMeter.Mark(Item (Some msgType.Name)) logger.Fail e
ãããæ©èœãããããã«å°ãæ®ã£ãŠããŸãã ãã³ãã©ãŒã®ã©ãããŒãä»ããŠãã¹ãŠããã€ã³ãããŸãã
let wrapHandler handler mb (logBuilder: unit -> #ILogBuilder) = let innherHandler mb msg = let logger = logBuilder() let msgType = msg.GetType() logger.Set (MessageType msgType) try try logger.OnOperationBegin() handler mb msg logger with | e -> registerExn msgType e logger; reraise() finally completeOperation msgType logger innherHandler mb
wrapHandlerã«ã¯è€éãªçœ²åããããŸãã CïŒã§ã¯ã次ã®ããã«ãªããŸãã
Func<TMsg, TResult> wrapHandler<Tmsg, TResult, TLogBuilder, TMailbox>( Func<TMailbox, TMsg, TLogBuilder, TResult> handler, TMailbox mb, Func<TLogBuilder> logBuilder) where TLogBuilder: ILogBuilder
åæã«ãä»ã®ãã¹ãŠã®ã¿ã€ãã«
å¶éã¯ãã
ãŸãã ã
æå³ã«é¢ããŠã¯ãwrapHandlerã¯TMsgãåä¿¡ããŠââTResultsãçæããé¢æ°ãåºåããå¿
èŠããããŸãã ãã®é¢æ°ã®æé ã¯æ¬¡ã®ãšããã§ãã
- ãã®ã³ã°æäœãéå§ããŸã
- æäœãè¡ããŸã
- ãã³ãã©ãŒã§åŠçãããªãäŸå€ãçºçããå Žåã¯ããã°ã«èšé²ããŠãã®ã¢ã¯ã¿ãŒã®èŠªã«è»¢éããŸã
- ãã®ã³ã°ãçµäºããŸã
ExpressionãActionã«å€æãããã¬ãŒã®æ°ããã€ã³ã¹ã¿ã³ã¹ã®ã¢ã¯ã¿ãŒãåã¢ã¯ã·ã§ã³ã«éä¿¡ããã«ã¯ãå¥ã®è£å©é¢æ°ãäœæããŸãã
let wrapExpr (expr: Expr<_,_>) mailbox logger = let action = expr.Compile() wrapHandler (fun mb msg log -> action.Invoke(mailbox, msg, log)) mailbox (fun () -> new LogBuilder(logger))
ãã®äžã§ãExpressionãååŸããŠã³ã³ãã€ã«ããã¡ãŒã«ããã¯ã¹ãšæ°ããLogBuilderïŒïŒãååŸããé¢æ°ãšå
±ã«ãäžèšã®wrapHandlerã«éä¿¡ããŸãã
ãã®ã¡ãœããã®çœ²åãåçŽã§ã¯ãããŸããã CïŒã§ã¯ã次ã®ããã«ãªããŸãã
Action<TMsg> wrapExpr<TMsg>( Expr<TMsg, LogBuilder> expr, Actor<TMsg> mb, ILoggingAdapterlogger)
TMsgã«ã¯ãŸã å¶éããããŸããã
ååž°é¢æ°ãäœæããããã ãã«æ®ã£ãŠããŸã:)
let rec loop() = actor { let! msg = mailbox.Receive() wrapExpr handler mailbox akkaLogger msg return! loop() } loop()
ãã®è¡šçŸãwrapExprãã³ãã©ã¡ãŒã«ããã¯ã¹akkaLoggerãã¯ãäžèšã®èª¬æãããããããã«ãActionãè¿ããŸãã ä»»æã®ã¿ã€ããå
¥åã«éä¿¡ããŠãŠããããååŸã§ããã¡ãœããïŒcïŒã®voidïŒã
æåŸã«åŒãmsgããè¿œå ããããmsgåŒæ°ããã®é¢æ°ã«ã¹ããŒããåä¿¡ããã¡ãã»ãŒãžã«å¯ŸããŠã¢ã¯ã·ã§ã³ãå®è¡ããŸãã
ã¿ã¹ã¯ãã³ãŒãã£ã³ã°ããŠãããçµäºããäŸã«é²ã¿ãŸãïŒ
ããããã¹ãŠéå§ããæ¹æ³ã¯ïŒ
ãããæ©èœããããã«ã¯ãå€ãã®ã³ãŒããæžãå¿
èŠã¯ãããŸããã
éåžžãã¡ãŒã«ããã¯ã¹ããã¬ãŒããŸãã¯ãšã©ãŒåŠçãå¿
èŠã§ããããšãç¥ããªããŠããã¡ãã»ãŒãžãã³ãã©ãŒã®ã¿ãèšè¿°ããããšãã§ããŸãã
ç°¡åãªå Žåã¯æ¬¡ã®ããã«ãªããŸãã
type ActorMessages = | Wait of int | Stop let waitProcess = function | Wait d -> Async.Sleep d |> Async.RunSynchronously | Stop -> ()
ãããŠããã®é¢æ°ãloggerActorã§ã©ããããç§ãã¡ãäžçæžåœåªåãããã¹ãŠã®ã°ããºãååŸããã«ã¯ã次ã®ããã«èšè¿°ã§ããŸãã
let spawnWaitWorker() = loggerActor <| Wrap.Handler(fun mb msg log -> waitProcess msg) let waitWorker = spawn system "worker-wait" <| spawnWaitWorker() waitWorker <! Wait 1000
è€éãªããžãã¯ããããã¡ãŒã«ããã¯ã¹ãšãã¬ãŒã«ã¢ã¯ã»ã¹ããå¿
èŠãããå ŽåïŒ
let failOrStopProcess (mailbox: Actor<_>) msg (log: ILogBuilder) = try match msg with | Wait d -> failwith "can't wait!" | Stop -> mailbox.Context.Stop mailbox.Self with | e -> log.Fail e let spawnFailOrStopWorker() = loggerActor <| Wrap.Handler(fun mb msg log -> failOrStopProcess mb msg log) let failOrStopWorker = spawn system "worker-vocal" <| spawnFailOrStopWorker() failOrStopWorker <! Wait 1000 // "can't wait!" failOrStopWorker <! Wait 500 // "can't wait!" failOrStopWorker <! Stop failOrStopWorker <! Wait 500 // DeadLetters
ããã°ã©ã èªäœã®EntryPointãActorSystemã®äœæãã¡ããªãã¯ãšã¢ã¯ã¿ãŒã®åŒãäžãã¯ããã¿ãã¬ã®äžã§èŠãããšãã§ããŸãããç®ç«ã£ããã®ã¯ãããŸããã
Program.fs open Akka.FSharp open SimpleInjector open App.Metrics; open Microsoft.Extensions.DependencyInjection open SimpleInjector.Integration.WebApi open System.Reflection open System open Metrics.MetricActors open ExampleActors let createSystem = let configStr = System.IO.File.ReadAllText("system.json") System.create "system-for-metrics" (Configuration.parse(configStr)) let createMetricActors system container = let dependencyResolver = new SimpleInjectorWebApiDependencyResolver(container) let apiConfig = { new IMetricApiConfig with member x.Host = "localhost" member x.Port = 10001 } let metricsReaderSpawner = createReader apiConfig dependencyResolver let metricsReader = spawn system "metrics-reader" metricsReaderSpawner let metricsRecorderSpawner = createRecorder (container.GetInstance<IMetrics>()) let metricsRecorder = spawn system "metrics-recorder" metricsRecorderSpawner () type Container with member x.AddMetrics() = let serviceCollection = new ServiceCollection() let entryAssemblyName = Assembly.GetEntryAssembly().GetName() let metricsHostBuilder = serviceCollection.AddMetrics(entryAssemblyName) serviceCollection.AddLogging() |> ignore let provider = serviceCollection.BuildServiceProvider() x.Register(fun () -> provider.GetRequiredService<IMetrics>()) [<EntryPoint>] let main argv = let container = new Container() let system = createSystem container.RegisterSingleton system container.AddMetrics() container.Verify() createMetricActors system container let waitWorker1 = spawn system "worker-wait1" <| spawnWaitWorker() let waitWorker2 = spawn system "worker-wait2" <| spawnWaitWorker() let waitWorker3 = spawn system "worker-wait3" <| spawnWaitWorker() let waitWorker4 = spawn system "worker-wait4" <| spawnWaitWorker() let failWorker = spawn system "worker-fail" <| spawnFailWorker() let waitOrStopWorker = spawn system "worker-silent" <| spawnWaitOrStopWorker() let failOrStopWorker = spawn system "worker-vocal" <| spawnFailOrStopWorker() waitWorker1 <! Wait 1000 waitWorker2 <! Wait 500 waitWorker3 <! Wait 5000 waitWorker4 <! Wait 8000 failWorker <! Wait 5000 waitOrStopWorker <! Wait 1000 waitOrStopWorker <! Wait 500 waitOrStopWorker <! Stop waitOrStopWorker <! Wait 500 failOrStopWorker <! Wait 1000 failOrStopWorker <! Wait 500 failOrStopWorker <! Stop failOrStopWorker <! Wait 500 Console.ReadKey() |> ignore 0
æãéèŠãªããšã¯ã¡ããªãã¯ã§ãïŒ
æäœäžã«localhostïŒ10001 / metricsãªã³ã¯ã«ç§»åãããšãååãªå€§ããã®jsonã衚瀺ãããããã«ã¯å€ãã®æ
å ±ããããŸãã waitProcessé¢æ°ã®äžéšã次ã«ç€ºããŸãã
é衚瀺ã®ããã¹ã { "Context": "waitProcess", "Counters": [ { "Name": "Instances Counter", "Unit": "items", "Count": 4 } ], "Meters": [ { "Name": "Message Processing Rate", "Unit": "items", "Count": 4, "FifteenMinuteRate": 35.668327519112893, "FiveMinuteRate": 35.01484385742755, "Items": [ { "Count": 4, "FifteenMinuteRate": 0.0, "FiveMinuteRate": 0.0, "Item": "Wait", "MeanRate": 13.082620551464204, "OneMinuteRate": 0.0, "Percent": 100.0 } ], "MeanRate": 13.082613248856632, "OneMinuteRate": 31.356094372926623, "RateUnit": "min" } ], "Timers": [ { "Name": "Operation Durations", "Unit": "req", "ActiveSessions": 0, "Count": 4, "DurationUnit": "ms", "Histogram": { "LastUserValue": "waitProcess", "LastValue": 8001.0, "Max": 8001.0, "MaxUserValue": "waitProcess", "Mean": 3927.1639786164278, "Median": 5021.0, "Min": 1078.0, "MinUserValue": "waitProcess", "Percentile75": 8001.0, "Percentile95": 8001.0, "Percentile98": 8001.0, "Percentile99": 8001.0, "Percentile999": 8001.0, "SampleSize": 4, "StdDev": 2932.0567172627871, "Sum": 15190.0 }, "Rate": { "FifteenMinuteRate": 0.00059447212531854826, "FiveMinuteRate": 0.00058358073095712587, "MeanRate": 0.00021824579927905906, "OneMinuteRate": 0.00052260157288211038 } } ] }
ããããããªãã¯ãããèŠã€ããããšãã§ããŸãïŒ
- çŸåšã4ã€ã®workProcessã€ã³ã¹ã¿ã³ã¹ããããŸãã
- 4ã€ã®åŸ
æ©ã¡ãã»ãŒãžãåŠçããŸããã
- ã¡ãã»ãŒãžåŠçæéã®äžå€®å€5021ããªç§
ã³ã³ãœãŒã«ã¯æ¬¡ã®ããã«ãªããŸãã
ãããã«
ãã®èšäºã«ã¯å€ãã®ã³ãŒããããã説æã¯ã»ãšãã©ãããŸããïŒæ確ã§ãªãå Žåã¯ã³ã¡ã³ãã§çããŸãïŒããããã¯ãã®èšäºãå®éã®ãããžã§ã¯ãã®ããã€ãã®æ¥åžžçãªã¿ã¹ã¯ã®è§£æ±ºçã瀺ãããšãç®çãšããŠããããã§ãã
ç¹ã«ãã®ã³ãŒãã¯å
ã
CïŒã®ã¢ã¯ã¿ãŒåãã«æžãããŠããããã誰ãã圹ã«ç«ã€ãããããŸãããå¿
èŠã«å¿ããŠããããã¹ãŠè»¢éã§ããŸãïŒãã³ãããäŒãããŸãã
è€éãªãã¡ã€ã³ã¢ãã«ã®ã¢ããªã³ã°ã«æºãã£ãŠãã人ã«ã¯ã åã·ã¹ãã ã¯ã¯ããã«è±å¯ã§ãããnullãš
åã®èšèšããªããããã¢ãã«ãããã°ã©ããŒã®ãšã©ãŒã«èããããšãã§ããŸãã
äŸã®ãããªããžããªã¯
ãã¡ãã§ãã
ãæž
èŽããããšãããããŸããïŒ