Filtrare e pre-elaborare i dati di telemetria in Application Insights SDK
È possibile scrivere codice per filtrare, modificare o arricchire i dati di telemetria prima dell'invio dall'SDK. L'elaborazione include i dati inviati dai moduli di telemetria standard, come la raccolta delle richieste HTTP e la raccolta delle dipendenze.
L'applicazione di filtri consente di modificare o rimuovere i dati di telemetria prima che vengano inviati dall'SDK implementando
ITelemetryProcessor
. È possibile, ad esempio, ridurre il volume della telemetria escludendo le richieste dei robot. A differenza del campionamento, si ha il controllo completo su ciò che viene inviato o rimosso, ma influisce su qualsiasi metrica basata su log aggregati. A seconda di come si eliminano gli elementi, si potrebbe inoltre perdere la possibilità di navigare tra elementi correlati.Aggiungere o modificare le proprietà di tutti i dati di telemetria inviati dall'app implementando un
ITelemetryInitializer
. È possibile, ad esempio, aggiungere valori calcolati oppure i numeri di versione in base a cui filtrare i dati nel portale.campionamento riduce il volume della telemetria senza effetti sulle statistiche. Tiene insieme i punti dati correlati per poter passare da uno all'altro quando si diagnostica un problema. Nel portale i conteggi totali vengono moltiplicati per compensare il campionamento.
Nota
L'API SDK viene usata per inviare metriche ed eventi personalizzati.
Prerequisiti
Installare l'SDK appropriato per l'applicazione: ASP.NET, ASP.NET Core, non HTTP/Worker per .NET/.NET Core o JavaScript.
Filtri
Questa tecnica offre un controllo diretto su ciò che viene incluso o escluso dal flusso di telemetria. Il filtro può essere usato per eliminare gli elementi di telemetria dall'invio ad Application Insights. È possibile usare il filtro con il campionamento o separatamente.
Per filtrare la telemetria, scrivere un processore di telemetria e registrarlo con TelemetryConfiguration
. Tutti i dati di telemetria passano attraverso il processore. È possibile scegliere di rilasciarlo dal flusso o assegnarlo al processore successivo nella catena. I dati di telemetria dai moduli standard, ad esempio l'agente di raccolta richieste HTTP e l'agente di raccolta dipendenze, e i dati di telemetria rilevati manualmente sono inclusi. Ad esempio, è possibile filtrare la telemetria sulle richieste dei robot o le chiamate di dipendenza riuscite.
Avviso
Se si filtra la telemetria inviata dall'SDK usando i processori, le statistiche visualizzate nel portale possono essere alterate e può risultare difficile seguire gli elementi correlati.
In alternativa, valutare la possibilità di usare il campionamento.
Applicazioni .NET
Implementare
ITelemetryProcessor
.I processori di telemetria creano una catena di elaborazione. Quando si crea un'istanza di un processore di telemetria, viene fornito un riferimento al processore successivo nella catena. Quando un punto dati di telemetria viene passato al metodo di processo, esegue il lavoro e quindi chiama (o non chiama) il processore di telemetria successivo nella catena.
using Microsoft.ApplicationInsights.Channel; using Microsoft.ApplicationInsights.Extensibility; using Microsoft.ApplicationInsights.DataContracts; public class SuccessfulDependencyFilter : ITelemetryProcessor { private ITelemetryProcessor Next { get; set; } // next will point to the next TelemetryProcessor in the chain. public SuccessfulDependencyFilter(ITelemetryProcessor next) { this.Next = next; } public void Process(ITelemetry item) { // To filter out an item, return without calling the next processor. if (!OKtoSend(item)) { return; } this.Next.Process(item); } // Example: replace with your own criteria. private bool OKtoSend (ITelemetry item) { var dependency = item as DependencyTelemetry; if (dependency == null) return true; return dependency.Success != true; } }
Aggiungere il processore.
Inserire questo frammento di codice in ApplicationInsights.config:
<TelemetryProcessors> <Add Type="WebApplication9.SuccessfulDependencyFilter, WebApplication9"> <!-- Set public property --> <MyParamFromConfigFile>2-beta</MyParamFromConfigFile> </Add> </TelemetryProcessors>
È possibile passare i valori della stringa dal file .config fornendo proprietà denominate come pubbliche nella classe.
Avviso
Prestare attenzione a fare corrispondere il nome del tipo e i nomi delle proprietà nel file. config ai nomi di classe e di proprietà nel codice. Se il file. config fa riferimento a un tipo inesistente o una proprietà, l’SDK potrebbe automaticamente non riuscire a inviare nessuna telemetria.
In alternativa , è possibile inizializzare il filtro nel codice. In una classe di inizializzazione adatta, ad esempio AppStart in
Global.asax.cs
, inserire il processore nella catena:var builder = TelemetryConfiguration.Active.DefaultTelemetrySink.TelemetryProcessorChainBuilder; builder.Use((next) => new SuccessfulDependencyFilter(next)); // If you have more processors: builder.Use((next) => new AnotherProcessor(next)); builder.Build();
I client di telemetria creati dopo questo punto usano i processori.
Filtri di esempio
Richieste sintetiche
Filtrare i robot e i test Web. Anche se Esplora metriche offre la possibilità di filtrare le origini sintetiche, questa opzione riduce il traffico e le dimensioni di inserimento filtrandoli nell'SDK stesso.
public void Process(ITelemetry item)
{
if (!string.IsNullOrEmpty(item.Context.Operation.SyntheticSource)) {return;}
// Send everything else:
this.Next.Process(item);
}
Autenticazione non riuscita
Filtrare le richieste con una risposta "401".
public void Process(ITelemetry item)
{
var request = item as RequestTelemetry;
if (request != null &&
request.ResponseCode.Equals("401", StringComparison.OrdinalIgnoreCase))
{
// To filter out an item, return without calling the next processor.
return;
}
// Send everything else
this.Next.Process(item);
}
Filtrare le chiamate di dipendenza remote rapide
Se si desidera diagnosticare solo le chiamate lente, filtrarne le veloci.
Nota
Questo filtro inclina le statistiche visualizzate nel portale.
public void Process(ITelemetry item)
{
var request = item as DependencyTelemetry;
if (request != null && request.Duration.TotalMilliseconds < 100)
{
return;
}
this.Next.Process(item);
}
Diagnosticare i problemi di dipendenza
Questo blog descrive un progetto per diagnosticare i problemi di dipendenza con l'invio automatico di ping regolari alle dipendenze.
Applicazioni Java
Per altre informazioni sui processori di telemetria e sulla relativa implementazione in Java, fare riferimento alla documentazione relativa ai processori di telemetria Java.
Applicazioni Web JavaScript
È possibile filtrare i dati di telemetria dalle applicazioni Web JavaScript usando ITelemetryInitializer.
Creare una funzione di callback dell'inizializzatore di telemetria. La funzione di callback accetta
ITelemetryItem
come parametro, ovvero l'evento in fase di elaborazione. Se si restituiscefalse
da questo callback, l'elemento di telemetria viene filtrato.var filteringFunction = (envelope) => { if (envelope.data.someField === 'tobefilteredout') { return false; } return true; };
Aggiungere il callback dell'inizializzatore di telemetria:
appInsights.addTelemetryInitializer(filteringFunction);
Aggiungere/modificare le proprietà: ITelemetryInitializer
Usare gli inizializzatori di telemetria per arricchire i dati di telemetria con informazioni aggiuntive o per eseguire l'override delle proprietà di telemetria impostate dai moduli di telemetria standard.
Ad esempio, Application Insights per un pacchetto Web raccoglie i dati di telemetria sulle richieste HTTP. Per impostazione predefinita, contrassegna qualsiasi richiesta con un codice di risposta >=400 come non riuscita. Se invece si vuole considerare 400 come un risultato positivo, è possibile fornire un inizializzatore di telemetria che imposti la proprietà Success.
In tal modo, l'inizializzatore di telemetria verrà chiamato ogni volta che viene chiamato uno dei metodi Track*(). Questo inizializzatore include Track()
metodi chiamati dai moduli di telemetria standard. Per convenzione, questi moduli non impostano alcuna proprietà già impostata da un inizializzatore. Gli inizializzatori di telemetria vengono chiamati prima di chiamare i processori di telemetria, quindi tutti gli arricchimenti eseguiti dagli inizializzatori sono visibili ai processori.
Applicazioni .NET
Definire l'inizializzatore
using System; using Microsoft.ApplicationInsights.Channel; using Microsoft.ApplicationInsights.DataContracts; using Microsoft.ApplicationInsights.Extensibility; namespace MvcWebRole.Telemetry { /* * Custom TelemetryInitializer that overrides the default SDK * behavior of treating response codes >= 400 as failed requests * */ public class MyTelemetryInitializer : ITelemetryInitializer { public void Initialize(ITelemetry telemetry) { var requestTelemetry = telemetry as RequestTelemetry; // Is this a TrackRequest() ? if (requestTelemetry == null) return; int code; bool parsed = Int32.TryParse(requestTelemetry.ResponseCode, out code); if (!parsed) return; if (code >= 400 && code < 500) { // If we set the Success property, the SDK won't change it: requestTelemetry.Success = true; // Allow us to filter these requests in the portal: requestTelemetry.Properties["Overridden400s"] = "true"; } // else leave the SDK to set the Success property } } }
Caricare l'inizializzatore
In ApplicationInsights.config:
<ApplicationInsights> <TelemetryInitializers> <!-- Fully qualified type name, assembly name: --> <Add Type="MvcWebRole.Telemetry.MyTelemetryInitializer, MvcWebRole"/> ... </TelemetryInitializers> </ApplicationInsights>
In alternativa, è possibile creare un'istanza dell'inizializzatore nel codice, ad esempio nel file Global.aspx.cs:
protected void Application_Start() { // ... TelemetryConfiguration.Active.TelemetryInitializers.Add(new MyTelemetryInitializer()); }
Vedere questo esempio nel dettaglio.
Inizializzatori di telemetria JavaScript
Inserire un inizializzatore di telemetria JavaScript, se necessario. Per altre informazioni sugli inizializzatori di telemetria per SDK di JavaScript per Application Insights, vedere inizializzatori di telemetria.
Inserire un inizializzatore di telemetria aggiungendo la funzione di callback onInit nella configurazione dello script di caricamento dell'SDK di JavaScript (Web):
<script type="text/javascript">
!(function (cfg){function e(){cfg.onInit&&cfg.onInit(n)}var x,w,D,t,E,n,C=window,O=document,b=C.location,q="script",I="ingestionendpoint",L="disableExceptionTracking",j="ai.device.";"instrumentationKey"[x="toLowerCase"](),w="crossOrigin",D="POST",t="appInsightsSDK",E=cfg.name||"appInsights",(cfg.name||C[t])&&(C[t]=E),n=C[E]||function(g){var f=!1,m=!1,h={initialize:!0,queue:[],sv:"8",version:2,config:g};function v(e,t){var n={},i="Browser";function a(e){e=""+e;return 1===e.length?"0"+e:e}return n[j+"id"]=i[x](),n[j+"type"]=i,n["ai.operation.name"]=b&&b.pathname||"_unknown_",n["ai.internal.sdkVersion"]="javascript:snippet_"+(h.sv||h.version),{time:(i=new Date).getUTCFullYear()+"-"+a(1+i.getUTCMonth())+"-"+a(i.getUTCDate())+"T"+a(i.getUTCHours())+":"+a(i.getUTCMinutes())+":"+a(i.getUTCSeconds())+"."+(i.getUTCMilliseconds()/1e3).toFixed(3).slice(2,5)+"Z",iKey:e,name:"Microsoft.ApplicationInsights."+e.replace(/-/g,"")+"."+t,sampleRate:100,tags:n,data:{baseData:{ver:2}},ver:undefined,seq:"1",aiDataContract:undefined}}var n,i,t,a,y=-1,T=0,S=["js.monitor.azure.com","js.cdn.applicationinsights.io","js.cdn.monitor.azure.com","js0.cdn.applicationinsights.io","js0.cdn.monitor.azure.com","js2.cdn.applicationinsights.io","js2.cdn.monitor.azure.com","az416426.vo.msecnd.net"],o=g.url||cfg.src,r=function(){return s(o,null)};function s(d,t){if((n=navigator)&&(~(n=(n.userAgent||"").toLowerCase()).indexOf("msie")||~n.indexOf("trident/"))&&~d.indexOf("ai.3")&&(d=d.replace(/(\/)(ai\.3\.)([^\d]*)$/,function(e,t,n){return t+"ai.2"+n})),!1!==cfg.cr)for(var e=0;e<S.length;e++)if(0<d.indexOf(S[e])){y=e;break}var n,i=function(e){var a,t,n,i,o,r,s,c,u,l;h.queue=[],m||(0<=y&&T+1<S.length?(a=(y+T+1)%S.length,p(d.replace(/^(.*\/\/)([\w\.]*)(\/.*)$/,function(e,t,n,i){return t+S[a]+i})),T+=1):(f=m=!0,s=d,!0!==cfg.dle&&(c=(t=function(){var e,t={},n=g.connectionString;if(n)for(var i=n.split(";"),a=0;a<i.length;a++){var o=i[a].split("=");2===o.length&&(t[o[0][x]()]=o[1])}return t[I]||(e=(n=t.endpointsuffix)?t.location:null,t[I]="https://"+(e?e+".":"")+"dc."+(n||"services.visualstudio.com")),t}()).instrumentationkey||g.instrumentationKey||"",t=(t=(t=t[I])&&"/"===t.slice(-1)?t.slice(0,-1):t)?t+"/v2/track":g.endpointUrl,t=g.userOverrideEndpointUrl||t,(n=[]).push((i="SDK LOAD Failure: Failed to load Application Insights SDK script (See stack for details)",o=s,u=t,(l=(r=v(c,"Exception")).data).baseType="ExceptionData",l.baseData.exceptions=[{typeName:"SDKLoadFailed",message:i.replace(/\./g,"-"),hasFullStack:!1,stack:i+"\nSnippet failed to load ["+o+"] -- Telemetry is disabled\nHelp Link: https://go.microsoft.com/fwlink/?linkid=2128109\nHost: "+(b&&b.pathname||"_unknown_")+"\nEndpoint: "+u,parsedStack:[]}],r)),n.push((l=s,i=t,(u=(o=v(c,"Message")).data).baseType="MessageData",(r=u.baseData).message='AI (Internal): 99 message:"'+("SDK LOAD Failure: Failed to load Application Insights SDK script (See stack for details) ("+l+")").replace(/\"/g,"")+'"',r.properties={endpoint:i},o)),s=n,c=t,JSON&&((u=C.fetch)&&!cfg.useXhr?u(c,{method:D,body:JSON.stringify(s),mode:"cors"}):XMLHttpRequest&&((l=new XMLHttpRequest).open(D,c),l.setRequestHeader("Content-type","application/json"),l.send(JSON.stringify(s)))))))},a=function(e,t){m||setTimeout(function(){!t&&h.core||i()},500),f=!1},p=function(e){var n=O.createElement(q),e=(n.src=e,t&&(n.integrity=t),n.setAttribute("data-ai-name",E),cfg[w]);return!e&&""!==e||"undefined"==n[w]||(n[w]=e),n.onload=a,n.onerror=i,n.onreadystatechange=function(e,t){"loaded"!==n.readyState&&"complete"!==n.readyState||a(0,t)},cfg.ld&&cfg.ld<0?O.getElementsByTagName("head")[0].appendChild(n):setTimeout(function(){O.getElementsByTagName(q)[0].parentNode.appendChild(n)},cfg.ld||0),n};p(d)}cfg.sri&&(n=o.match(/^((http[s]?:\/\/.*\/)\w+(\.\d+){1,5})\.(([\w]+\.){0,2}js)$/))&&6===n.length?(d="".concat(n[1],".integrity.json"),i="@".concat(n[4]),l=window.fetch,t=function(e){if(!e.ext||!e.ext[i]||!e.ext[i].file)throw Error("Error Loading JSON response");var t=e.ext[i].integrity||null;s(o=n[2]+e.ext[i].file,t)},l&&!cfg.useXhr?l(d,{method:"GET",mode:"cors"}).then(function(e){return e.json()["catch"](function(){return{}})}).then(t)["catch"](r):XMLHttpRequest&&((a=new XMLHttpRequest).open("GET",d),a.onreadystatechange=function(){if(a.readyState===XMLHttpRequest.DONE)if(200===a.status)try{t(JSON.parse(a.responseText))}catch(e){r()}else r()},a.send())):o&&r();try{h.cookie=O.cookie}catch(k){}function e(e){for(;e.length;)!function(t){h[t]=function(){var e=arguments;f||h.queue.push(function(){h[t].apply(h,e)})}}(e.pop())}var c,u,l="track",d="TrackPage",p="TrackEvent",l=(e([l+"Event",l+"PageView",l+"Exception",l+"Trace",l+"DependencyData",l+"Metric",l+"PageViewPerformance","start"+d,"stop"+d,"start"+p,"stop"+p,"addTelemetryInitializer","setAuthenticatedUserContext","clearAuthenticatedUserContext","flush"]),h.SeverityLevel={Verbose:0,Information:1,Warning:2,Error:3,Critical:4},(g.extensionConfig||{}).ApplicationInsightsAnalytics||{});return!0!==g[L]&&!0!==l[L]&&(e(["_"+(c="onerror")]),u=C[c],C[c]=function(e,t,n,i,a){var o=u&&u(e,t,n,i,a);return!0!==o&&h["_"+c]({message:e,url:t,lineNumber:n,columnNumber:i,error:a,evt:C.event}),o},g.autoExceptionInstrumented=!0),h}(cfg.cfg),(C[E]=n).queue&&0===n.queue.length?(n.queue.push(e),n.trackPageView({})):e();})({
src: "https://js.monitor.azure.com/scripts/b/ai.3.gbl.min.js",
crossOrigin: "anonymous", // When supplied this will add the provided value as the cross origin attribute on the script tag
onInit: function (sdk) {
sdk.addTelemetryInitializer(function (envelope) {
envelope.data = envelope.data || {};
envelope.data.someField = 'This item passed through my telemetry initializer';
});
}, // Once the application insights instance has loaded and initialized this method will be called
// sri: false, // Custom optional value to specify whether fetching the snippet from integrity file and do integrity check
cfg: { // Application Insights Configuration
connectionString: "YOUR_CONNECTION_STRING"
}});
</script>
Per un riepilogo delle proprietà non personalizzate disponibili nell'elemento di telemetria, vedere Modello di dati di esportazione di Application Insights.
È possibile aggiungere tutti gli inizializzatori desiderati. Sono chiamati nell'ordine in cui vengono aggiunti.
Processori di telemetria Python OpenCensus
I processori di telemetria in OpenCensus Python sono semplicemente funzioni di callback chiamate per elaborare i dati di telemetria prima dell'esportazione. La funzione di callback deve accettare una busta tipo di dati come parametro. Per escludere i dati di telemetria dall'esportazione, assicurarsi che la funzione di callback restituisca False
. È possibile visualizzare lo schema per i tipi di dati di Monitoraggio di Azure nelle buste in GitHub.
Nota
È possibile modificare cloud_RoleName
modificando l'attributo ai.cloud.role
nel campo tags
.
def callback_function(envelope):
envelope.tags['ai.cloud.role'] = 'new_role_name'
# Example for log exporter
import logging
from opencensus.ext.azure.log_exporter import AzureLogHandler
logger = logging.getLogger(__name__)
# Callback function to append '_hello' to each log message telemetry
def callback_function(envelope):
envelope.data.baseData.message += '_hello'
return True
handler = AzureLogHandler(connection_string='InstrumentationKey=<your-instrumentation_key-here>')
handler.add_telemetry_processor(callback_function)
logger.addHandler(handler)
logger.warning('Hello, World!')
# Example for trace exporter
import requests
from opencensus.ext.azure.trace_exporter import AzureExporter
from opencensus.trace import config_integration
from opencensus.trace.samplers import ProbabilitySampler
from opencensus.trace.tracer import Tracer
config_integration.trace_integrations(['requests'])
# Callback function to add os_type: linux to span properties
def callback_function(envelope):
envelope.data.baseData.properties['os_type'] = 'linux'
return True
exporter = AzureExporter(
connection_string='InstrumentationKey=<your-instrumentation-key-here>'
)
exporter.add_telemetry_processor(callback_function)
tracer = Tracer(exporter=exporter, sampler=ProbabilitySampler(1.0))
with tracer.span(name='parent'):
response = requests.get(url='https://www.wikipedia.org/wiki/Rabbit')
# Example for metrics exporter
import time
from opencensus.ext.azure import metrics_exporter
from opencensus.stats import aggregation as aggregation_module
from opencensus.stats import measure as measure_module
from opencensus.stats import stats as stats_module
from opencensus.stats import view as view_module
from opencensus.tags import tag_map as tag_map_module
stats = stats_module.stats
view_manager = stats.view_manager
stats_recorder = stats.stats_recorder
CARROTS_MEASURE = measure_module.MeasureInt("carrots",
"number of carrots",
"carrots")
CARROTS_VIEW = view_module.View("carrots_view",
"number of carrots",
[],
CARROTS_MEASURE,
aggregation_module.CountAggregation())
# Callback function to only export the metric if value is greater than 0
def callback_function(envelope):
return envelope.data.baseData.metrics[0].value > 0
def main():
# Enable metrics
# Set the interval in seconds in which you want to send metrics
exporter = metrics_exporter.new_metrics_exporter(connection_string='InstrumentationKey=<your-instrumentation-key-here>')
exporter.add_telemetry_processor(callback_function)
view_manager.register_exporter(exporter)
view_manager.register_view(CARROTS_VIEW)
mmap = stats_recorder.new_measurement_map()
tmap = tag_map_module.TagMap()
mmap.measure_int_put(CARROTS_MEASURE, 1000)
mmap.record(tmap)
# Default export interval is every 15.0s
# Your application should run for at least this amount
# of time so the exporter will meet this interval
# Sleep can fulfill this
time.sleep(60)
print("Done recording metrics")
if __name__ == "__main__":
main()
È possibile aggiungere tutti i processori desiderati. Sono chiamati nell'ordine in cui vengono aggiunti. Se un processore genera un'eccezione, non influisce sui processori seguenti.
Esempio di TelemetryInitializer
Aggiungere una proprietà personalizzata
L'inizializzatore di esempio seguente aggiunge una proprietà personalizzata a ogni telemetria rilevata.
public void Initialize(ITelemetry item)
{
var itemProperties = item as ISupportProperties;
if(itemProperties != null && !itemProperties.Properties.ContainsKey("customProp"))
{
itemProperties.Properties["customProp"] = "customValue";
}
}
Aggiungere un nome di ruolo cloud
L'inizializzatore di esempio seguente imposta il nome del ruolo cloud su ogni telemetria rilevata.
public void Initialize(ITelemetry telemetry)
{
if (string.IsNullOrEmpty(telemetry.Context.Cloud.RoleName))
{
telemetry.Context.Cloud.RoleName = "MyCloudRoleName";
}
}
Controllare l'indirizzo IP client usato per i mapping di georilevazione
L'inizializzatore di esempio seguente imposta l'IP client, che viene usato per il mapping della georilevazione, anziché per l'indirizzo IP del socket client, durante l'inserimento dei dati di telemetria.
public void Initialize(ITelemetry telemetry)
{
var request = telemetry as RequestTelemetry;
if (request == null) return true;
request.Context.Location.Ip = "{client ip address}"; // Could utilize System.Web.HttpContext.Current.Request.UserHostAddress;
return true;
}
ITelemetryProcessor e ITelemetryInitializer
Qual è la differenza tra processori di telemetria e inizializzatori di telemetria?
- Ci sono alcune sovrapposizioni in ciò che è possibile fare. Entrambi possono essere usati per aggiungere o modificare le proprietà dei dati di telemetria, anche se è consigliabile usare gli inizializzatori a tale scopo.
- Gli inizializzatori di telemetria vengono sempre eseguiti prima dei processori di telemetria.
- Gli inizializzatori di telemetria possono essere chiamati più volte. Per convenzione, non impostano alcuna proprietà già impostata.
- I processori di telemetria consentono di sostituire o rimuovere completamente un elemento di telemetria.
- Tutti gli inizializzatori di telemetria registrati vengono chiamati per ogni elemento di telemetria. Per i processori di telemetria, SDK garantisce la chiamata al primo processore di telemetria. Se il resto dei processori viene chiamato o meno viene deciso dai processori di telemetria precedenti.
- Usare gli inizializzatori di telemetria per arricchire i dati di telemetria con più proprietà o eseguire l'override di uno esistente. Usare un processore di telemetria per filtrare i dati di telemetria.
Nota
JavaScript include solo inizializzatori di telemetria che possono filtrare gli eventi usando ITelemetryInitializer
Risolvere i problemi di ApplicationInsights.config
- Verificare che il nome di tipo completo e il nome di assembly siano corretti.
- Verificare che il file applicationinsights.config si trovi nella directory di output e includa le ultime modifiche apportate.