Skip to content

Instantly share code, notes, and snippets.

@ThomasArdal
Last active December 17, 2015 22:10
Show Gist options
  • Save ThomasArdal/5680420 to your computer and use it in GitHub Desktop.
Save ThomasArdal/5680420 to your computer and use it in GitHub Desktop.
using System;
using System.Collections;
using System.Configuration;
using System.Globalization;
using Nest;
namespace Elmah.ElasticSearch
{
public class ElasticSearchLog : ErrorLog
{
ElasticClient _elasticClient;
public ElasticSearchLog(IDictionary config)
{
if (config == null)
{
throw new ArgumentNullException("config");
}
InitElasticSearch(config);
}
public override string Log(Error error)
{
var indexResponse = _elasticClient.Index(new ErrorDocument
{
ApplicationName = ApplicationName,
Error = error,
ErrorXml = ErrorXml.EncodeString(error)
});
return indexResponse.Id;
}
public override ErrorLogEntry GetError(string id)
{
var document = _elasticClient.Get<ErrorDocument>(id);
var result = !string.IsNullOrEmpty(document.ErrorXml)
? new ErrorLogEntry(this, id, ErrorXml.DecodeString(document.ErrorXml))
: new ErrorLogEntry(this, id, document.Error);
return result;
}
public override int GetErrors(int pageIndex, int pageSize, IList errorEntryList)
{
var result = _elasticClient.Search<ErrorDocument>(x =>
{
if (!string.IsNullOrWhiteSpace(ApplicationName))
{
x.Filter(f => f.Term(t => t.ApplicationName, ApplicationName));
}
x.Skip(pageSize*pageIndex).Take(pageSize).Sort(s => s.OnField(e => e.Error.Time).Descending());
return x;
});
foreach (var errorDocument in result.Documents)
{
errorEntryList.Add(new ErrorLogEntry(this, errorDocument.Id.ToString(CultureInfo.InvariantCulture), errorDocument.Error));
}
return result.Total;
}
private string LoadConnectionString(IDictionary config)
{
// From ELMAH source
// First look for a connection string name that can be
// subsequently indexed into the <connectionStrings> section of
// the configuration to get the actual connection string.
var connectionStringName = (string)config["connectionStringName"];
if (!string.IsNullOrEmpty(connectionStringName))
{
var settings = ConfigurationManager.ConnectionStrings[connectionStringName];
if (settings != null)
return settings.ConnectionString;
throw new ApplicationException(string.Format("Could not find a ConnectionString with the name '{0}'.", connectionStringName));
}
throw new ApplicationException("You must specifiy the 'connectionStringName' attribute on the <errorLog /> element.");
}
private void InitElasticSearch(IDictionary config)
{
var defaultIndex = "elmah";
var url = LoadConnectionString(config);
var connectionSettings = new ConnectionSettings(new Uri(url));
connectionSettings.SetDefaultIndex(defaultIndex);
_elasticClient = new ElasticClient(connectionSettings);
ConnectionStatus status;
_elasticClient.TryConnect(out status);
if (!status.Success)
{
throw new ApplicationException(
string.Format("Could not connect to ElasticSearch: " +
(status.Error != null ? status.Error.ExceptionMessage : "Unknown reason")));
}
}
}
[ElasticType]
public class ErrorDocument
{
public int Id { get; set; }
public Error Error { get; set; }
public string ErrorXml { get; set; }
public string ApplicationName { get; set; }
}
}
@Mpdreamz
Copy link

or better through an explicit create index call

client.CreateIndex("myindexname", c => c
    .NumberOfReplicas(0)
    .NumberOfShards(1)
    .Settings(s=>s
        .Add("merge.policy.merge_factor","10")
        .Add("search.slowlog.threshold.fetch.warn", "1s")
    )   
    .AddMapping<ElasticSearchProject>(m => m.MapFromAttributes())
    .AddMapping<Person>(m => m.MapFromAttributes())
);

@Mpdreamz
Copy link

Yeah an IndexExists instead of tryconnect each time is a good idea. The indexExist is a HEAD and pretty light.

There is not really a connection to keep open, newing the client is just fine. The whole client is stateless.

The SO is interesting can you reproduce it and create an issue?

Laptop battery died and left the charger at work :( replying on my phone so excuse the short sentences/wording/tone :)

@ThomasArdal
Copy link
Author

The error is happening when mapping the Error object from Elmah. Must be a circular reference in there some place. I'm still not sure if I would like to continue adding the Elmah Error object to ES or converting it to properties on the ErrorDocument in my sample.

I would recommend you to steal borrow an extra from work. Saved me numerous times :)

@ThomasArdal
Copy link
Author

After some debugging I found out, that it's a property of type Exception, that causes the problem in MapFluent.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment