Log4net, eliminate duplicate messages - log4net-configuration

is there any option to setup log4net to eliminate duplicate messages? I have an application which works in cycles...there is an infinate while cycle. If any error occure (for example database is not accesible), same message is logged again and again in every loop. I need to log the message just once.

I have implemented log4net filter to eliminate same messages during specified time interval.
The class:
public class DuplicityFilter : FilterSkeleton
{
private String lastMessage = null;
private List<Tuple<DateTime, String>> lastMessages = new List<Tuple<DateTime,string>>();
private Int32 _timeWindow = 0;
public Int32 timeWindow
{
get { return _timeWindow; }
set { _timeWindow = value; }
}
public Boolean _lastOnly = false;
public Boolean lastOnly
{
get { return _lastOnly; }
set { _lastOnly = value; }
}
public override FilterDecision Decide(log4net.Core.LoggingEvent loggingEvent)
{
if (_lastOnly)
{
if (lastMessage == loggingEvent.RenderedMessage)
{
return FilterDecision.Deny;
}
else
{
lastMessage = loggingEvent.RenderedMessage;
return FilterDecision.Accept;
}
}
else
{
if (_timeWindow <= 0)
return FilterDecision.Accept;
// Removes old messages
lastMessages.RemoveAll(m => m.Item1 < DateTime.Now.AddSeconds(0 - _timeWindow));
if (!lastMessages.Any(m => m.Item2 == loggingEvent.RenderedMessage))
{
lastMessages.Add(new Tuple<DateTime, string>(loggingEvent.TimeStamp, loggingEvent.RenderedMessage));
return FilterDecision.Accept;
}
else
{
return FilterDecision.Deny;
}
}
}
And the XML definition:
<filter type="your.namespace.here.DuplicityFilter">
<timeWindow value="900" /> <!-- 15min -->
<lastOnly value="false" />
</filter>

Related

Exclude specific products from Product Indexer in Shopware 6

We have four specific products with a massive amount of variants. When running the Product Indexer we run out of memory because of these products.
So we want to exclude these specific products from the Product Indexer Job.
My first approach was to use the ProductIndexerEvent, but the event is dispatched at the end of the handle() method :
(vendor/shopware/core/Content/Product/DataAbstractionLayer/ProductIndexer.php:187),
which is probably too late.
What is the best approach to implement that behaviour?
I would advise against excluding products from being indexed. There's business logic relying on the data being indexed.
If you're confident in what you're doing and know about the consequences, you could decorate the ProductIndexer service.
<service id="Foo\MyPlugin\ProductIndexerDecorator" decorates="Shopware\Core\Content\Product\DataAbstractionLayer\ProductIndexer">
<argument type="service" id="Foo\MyPlugin\ProductIndexerDecorator.inner"/>
</service>
In the decorator you would have to deconstruct the original event, filter the WriteResult instances by excluded IDs and then pass the reconstructed event to the decorated service.
class ProductIndexerDecorator extends EntityIndexer
{
const FILTERED_IDS = ['9b180c61ddef4dad89e9f3b9fa13f3be'];
private EntityIndexer $decorated;
public function __construct(EntityIndexer $decorated)
{
$this->decorated = $decorated;
}
public function getDecorated(): EntityIndexer
{
return $this->decorated;
}
public function getName(): string
{
return $this->getDecorated()->getName();
}
public function iterate($offset): ?EntityIndexingMessage
{
return $this->getDecorated()->iterate($offset);
}
public function update(EntityWrittenContainerEvent $event): ?EntityIndexingMessage
{
$originalEvents = clone $event->getEvents();
if (!$originalEvents) {
return $this->getDecorated()->update($event);
}
$event->getEvents()->clear();
/** #var EntityWrittenEvent $writtenEvent */
foreach ($originalEvents as $writtenEvent) {
if ($writtenEvent->getEntityName() !== 'product') {
$event->getEvents()->add($writtenEvent);
continue;
}
$results = [];
foreach ($writtenEvent->getWriteResults() as $result) {
if (\in_array($result->getPrimaryKey(), self::FILTERED_IDS, true)) {
continue;
}
$results[] = $result;
}
$event->getEvents()->add(new EntityWrittenEvent('product', $results, $event->getContext()));
}
return $this->getDecorated()->update($event);
}
public function handle(EntityIndexingMessage $message): void
{
$data = array_diff($message->getData(), self::FILTERED_IDS);
$newMessage = new ProductIndexingMessage($data, $message->getOffset(), $message->getContext(), $message->forceQueue());
$this->getDecorated()->handle($newMessage);
}
public function getTotal(): int
{
return $this->getDecorated()->getTotal();
}
public function getOptions(): array
{
return $this->getDecorated()->getOptions();
}
}

Kotlin: correct way for read-only getters

I have a Java class (simplified) like that
public class Input {
private boolean leftPressed;
private boolean rightPressed;
public void handleValue(Event event) {
if (event.button == 1) {
leftPressed = event.pressed;
}
else if (event.button == 3) {
rightPressed = event.pressed;
}
}
public boolean isLeftPressed() {
return leftPressed;
}
public boolean isRightPressed() {
return rightPressed;
}
}
Note, that the fields only can be changed by code inside handleValue. Would this Kotlin code
class Input {
var leftPressed = false
private set
var rightPressed = false
private set
handleValue(event: Event) {
if (event.button == 1) {
leftPressed = event.pressed;
}
else if (event.button == 3) {
rightPressed = event.pressed;
}
}
}
be the proper way of creating the read-only properties? You really have to add the private set to make it safe/read-only? Are there other/better ways to prevent writing to the field from outside this class?

Is there a standard way to package many Restlets into a single Restlet?

I have a situation where the application developers and the framework provider are not the people. As a framework provider, I would like to be able to hand the developers what looks like a single Filter, but is in fact a chain of standard Filters (such as authentication, setting up invocation context, metrics, ++).
I don't seem to find this functionality in the standard library, but maybe there is an extension with it.
Instead of waiting for an answer, I went ahead with my own implementation and sharing here if some needs this.
/**
* Composes an array of Restlet Filters into a single Filter.
*/
public class ComposingFilter extends Filter
{
private final Filter first;
private final Filter last;
public ComposingFilter( Filter... composedOf )
{
Objects.requireNonNull( composedOf );
if( composedOf.length == 0 )
{
throw new IllegalArgumentException( "Filter chain can't be empty." );
}
first = composedOf[ 0 ];
Filter prev = first;
for( int i = 1; i < composedOf.length; i++ )
{
Filter next = composedOf[ i ];
prev.setNext( next );
prev = next;
}
last = composedOf[ composedOf.length - 1 ];
}
#Override
protected int doHandle( Request request, Response response )
{
if( first != null )
{
first.handle( request, response );
Response.setCurrent( response );
if( getContext() != null )
{
Context.setCurrent( getContext() );
}
}
else
{
response.setStatus( Status.SERVER_ERROR_INTERNAL );
getLogger().warning( "The filter " + getName() + " was executed without a next Restlet attached to it." );
}
return CONTINUE;
}
#Override
public synchronized void start()
throws Exception
{
if( isStopped() )
{
first.start();
super.start();
}
}
#Override
public synchronized void stop()
throws Exception
{
if( isStarted() )
{
super.stop();
first.stop();
}
}
#Override
public Restlet getNext()
{
return last.getNext();
}
#Override
public void setNext( Class<? extends ServerResource> targetClass )
{
last.setNext( targetClass );
}
#Override
public void setNext( Restlet next )
{
last.setNext( next );
}
}
NOTE: Not tested yet.

Moving from hard-coded to SOLID principles in PHP

I am actually reading theory about clean code and SOLID principles. I know understand well that we should program to an interface and not to an implementation.
So, I actually try to apply those principles to a little part of my code. I would like to have your advice or point of view so I can know if I am going in the good direction. I'll show you my previous code and my actual so you can visualize the evolution.
To start, i had a method in my controller to check some requirements for every step of an order process (4 steps that the user have to follow in the right order => 1 then 2 then 3 and then 4)
This is my old code :
private function isAuthorizedStep($stepNumber)
{
$isStepAccessAuthorized = TRUE;
switch($stepNumber) {
case self::ORDER_STEP_TWO: // ORDER_STEP_TWO = 2
if (!($_SESSION['actualOrderStep'] >= ORDER_STEP_ONE)) {
$isStepAccessAuthorized = FALSE;
}
break;
case self::ORDER_STEP_THREE:
if (!($_SESSION['actualOrderStep'] >= ORDER_STEP_TWO)) {
$isStepAccessAuthorized = FALSE;
}
break;
...
}
return $isStepAccessAuthorized;
}
public function orderStepTwo()
{
if ($this->isAuthorizedStep(self::ORDER_STEP_TWO) {
return;
}
... // do some stuff
// after all the verifications:
$_SESSION['actualOrderStep'] = ORDER_STEP_TWO
}
Trying to fit to SOLID principles, I splited my code following this logic:
Extracting hard-coded logic from controllers to put it in classes (reusability)
Using Dependency Injection and abstraction
interface RuleInterface {
public function matches($int);
}
class StepAccessControl
{
protected $rules;
public function __construct(array $rules)
{
foreach($rules as $key => $rule) {
$this->addRule($key, $rule);
}
}
public isAccessGranted($actualOrderStep)
{
$isAccessGranted = TRUE;
foreach($this->rules as $rule) {
if (!$rule->matches($actualOrderStep) {
$isAccessGranted = FALSE;
}
}
return $isAccessGranted;
}
public function addRule($key, RuleInterface $rule)
{
$this->rules[$key] = $rule;
}
}
class OrderStepTwoRule implements RuleInterface
{
public function matches($actualStep)
{
$matches = TRUE;
if (!($actualStep >= 1)) {
$isStepAccessAuthorized = FALSE;
}
return $matches;
}
}
class StepAccessControlFactory
{
public function build($stepNumber)
{
if ($stepNumber == 1) {
...
} elseif ($stepNumber == 2) {
$orderStepTwoRule = new OrderStepTwoRule();
return new StepAcessControl($orderStepTwoRule);
}...
}
}
and then in the controller :
public function stepTwoAction()
{
$stepAccessControlFactory = new StepAccessControlFactory();
$stepTwoAccessControl = $stepAccessControlFactory(2);
if (!$stepTwoAccessControl->isAccesGranted($_SESSION['actualOrderStep'])) {
return FALSE;
}
}
I would like to know if I get the spirit and if I am on the good way :)

Serializing properties

I have searched everywhere in CF docs, FAQ's, Blog and SO entries but still can't understand why this model:
<cf:project defaultNamespace="Humanisme" xmlns:cf="http://www.softfluent.com/codefluent/2005/1" xmlns:cfx="http://www.softfluent.com/codefluent/modeler/2008/1" xmlns:cfom="http://www.softfluent.com/codefluent/producers.model/2005/1" xmlns:cfps="http://www.softfluent.com/codefluent/producers.sqlserver/2005/1" xmlns:cfsm="http://www.softfluent.com/codefluent/producers.servicemodel/2007/1" defaultKeyPropertyTypeName="int" defaultMaxLength="240" persistencePropertyNameFormat="{1}" createDefaultMethodForms="true" createDefaultApplication="false" createDefaultHints="false">
<cf:import path="Default.Surface.cfp" />
<cf:producer name="Business Object Model (BOM)" typeName="CodeFluent.Producers.CodeDom.CodeDomProducer, CodeFluent.Producers.CodeDom">
<cf:configuration compileWithVisualStudio="true" compile="false" codeDomProviderTypeName="CSharp" targetDirectory="..\Humanisme.classes" produceWebMembershipProvider="false" produceWebProfileProvider="false" produceWebBasicAuthenticationModule="false" cfx:targetProject="..\Humanisme.classes\Humanisme.classes.csproj" cfx:targetProjectLayout="Update">
<subProducer typeName="CodeFluent.Producers.ServiceModel.ServiceProducer, CodeFluent.Producers.ServiceModel, Version=1.0.0.0, Culture=neutral, PublicKeyToken=1bb6d7cccf1045ec" compileWithVisualStudio="true" compile="false" codeDomProviderTypeName="CSharp" targetDirectory="..\Humanisme.webApi" silverlightTargetVersion="Unspecified" dataAnnotationsProductionModes="NoAnnotations, All" jsonOptions="EnableJson" outputName="HumanismeService" cfx:targetProject="..\Humanisme.webApi\Humanisme.webApi.csproj" cfx:targetProjectLayout="Update" produceProxy="False" />
</cf:configuration>
</cf:producer>
(....)
<cf:entity name="EtapaVital" namespace="Humanisme" categoryPath="/Humanisme">
<cf:property name="Id" key="true" />
<cf:property name="Nom" />
<cf:property name="Idioma" typeName="{0}.EtapaVitalIdiomaCollection" relationPropertyName="Etapa" />
<cf:property name="Documents" typeName="{0}.DocumentCollection" relationPropertyName="EtapaVital" />
</cf:entity>
Renders to these serializing attributes:
RowVersion and EntityState get the serializing attributes.
EntityDisplayName does not.
[System.Runtime.Serialization.DataMemberAttribute(Order=2147483647)]
private CodeFluent.Runtime.CodeFluentEntityState _entityState;
public EtapaVital()
[System.Runtime.Serialization.DataMemberAttribute()]
public virtual string EntityKey
{
get
{
return this.Id.ToString();
}
set
{
this.Id = ((int)(ConvertUtilities.ChangeType(value, typeof(int), -1)));
}
}
public virtual string EntityDisplayName
{
get
{
return this.Nom;
}
}
[System.Xml.Serialization.XmlElementAttribute(IsNullable=true)]
[System.ComponentModel.DataObjectFieldAttribute(false, true)]
[System.ComponentModel.TypeConverterAttribute(typeof(CodeFluent.Runtime.Design.ByteArrayConverter))]
[System.Runtime.Serialization.DataMemberAttribute()]
public byte[] RowVersion
{
get
{
return this._rowVersion;
}
set
{
if (((value != null)
&& (value.Length == 0)))
{
value = null;
}
this._rowVersion = value;
this.OnPropertyChanged(new System.ComponentModel.PropertyChangedEventArgs("RowVersion"));
}
}
[System.ComponentModel.DefaultValueAttribute(((int)(-1)))]
[System.Xml.Serialization.XmlElementAttribute(IsNullable=false, Type=typeof(int))]
[System.ComponentModel.DataObjectFieldAttribute(true)]
[System.Runtime.Serialization.DataMemberAttribute(EmitDefaultValue=false, Order=100)]
public int Id
{
get
{
return this._id;
}
set
{
if ((System.Collections.Generic.EqualityComparer<int>.Default.Equals(value, this._id) == true))
{
return;
}
int oldKey = this._id;
this._id = value;
try
{
this.OnCollectionKeyChanged(oldKey);
}
catch (System.ArgumentException )
{
this._id = oldKey;
return;
}
this.EntityState = CodeFluent.Runtime.CodeFluentEntityState.Modified;
this.OnPropertyChanged(new System.ComponentModel.PropertyChangedEventArgs("Id"));
}
}
[System.ComponentModel.DefaultValueAttribute(default(string))]
[System.Xml.Serialization.XmlElementAttribute(IsNullable=true, Type=typeof(string))]
[System.Runtime.Serialization.DataMemberAttribute(EmitDefaultValue=false, Order=200)]
public string Nom
{
get
{
return this._nom;
}
set
{
this._nom = value;
this.EntityState = CodeFluent.Runtime.CodeFluentEntityState.Modified;
this.OnPropertyChanged(new System.ComponentModel.PropertyChangedEventArgs("Nom"));
}
}
[System.Xml.Serialization.XmlIgnoreAttribute()]
public Humanisme.EtapaVitalIdiomaCollection Idioma
{
get
{
if ((this._idioma == null))
{
if (((this.Id == -1)
|| (this.EntityState.Equals(CodeFluent.Runtime.CodeFluentEntityState.Created) == true)))
{
this._idioma = new Humanisme.EtapaVitalIdiomaCollection(this);
return this._idioma;
}
this._idioma = Humanisme.EtapaVitalIdiomaCollection.LoadByEtapa(this);
}
return this._idioma;
}
}
[System.Xml.Serialization.XmlIgnoreAttribute()]
public Humanisme.DocumentCollection Documents
{
get
{
if ((this._documents == null))
{
if (((this.Id == -1)
|| (this.EntityState.Equals(CodeFluent.Runtime.CodeFluentEntityState.Created) == true)))
{
this._documents = new Humanisme.DocumentCollection(null, this);
return this._documents;
}
this._documents = Humanisme.DocumentCollection.LoadByEtapaVital(this);
}
return this._documents;
}
}
public virtual CodeFluent.Runtime.CodeFluentEntityState EntityState
{
get
{
return this._entityState;
}
set
{
if ((System.Collections.Generic.EqualityComparer<CodeFluent.Runtime.CodeFluentEntityState>.Default.Equals(value, this.EntityState) == true))
{
return;
}
if (((this._entityState == CodeFluent.Runtime.CodeFluentEntityState.ToBeDeleted)
&& (value == CodeFluent.Runtime.CodeFluentEntityState.Modified)))
{
return;
}
if (((this._entityState == CodeFluent.Runtime.CodeFluentEntityState.Created)
&& (value == CodeFluent.Runtime.CodeFluentEntityState.Modified)))
{
return;
}
this._entityState = value;
this.OnPropertyChanged(new System.ComponentModel.PropertyChangedEventArgs("EntityState"));
}
}
It seems there are not related property options on the model surface "Properties" window and I'm not able to find the xml tags or attributes in the cfp file.
I will be very grateful if you can give me any clue or reference where to start learning the CodeFluent Model options that lead to these outputs in the classes.
Thanks again, I'm really turning into a CodeFluent enthusiast!
The RowVersion property is generated by CodeFluent Entities because you set the concurrency mode to Optimistic (documentation). If you don't have the latest value of this property, you cannot save the entity (CodeFluentConcurrencyException). So this property needs to be serialized.
The EntityDisplayName is for UI purposes. Its value is computed from one or many properties. So there is no need to serialize it as you can compute it client side.
There is currently no attributes to change the serialization behavior for these specific properties. However you can write a custom sub producer to add/remove serialization attributes on properties. You'll find an example that adds Json.NET specific attributes on properties (GitHub)
Here's are some attributes to change the serialization behavior:
serializable (Entity): Determines if this entity is marked as serializable
serializeEntityKey (Entity): Determines if the EntityKey must be serialized
serializeTypeName (Entity): Determines if the TypeName must be serialized
serializationMode (Property): documentation
dataMember or includeInSerialization (Property) => Prefer using serializationMode
serializationOrder (Property): Defines the sort order of this property when serialized
serializationNullable (Property): Determines if the property can be skipped when serialized
serializePrivateMember (Property): Determines if the private field of a non-serializable property is serializable
publicSerializationCallbacks (Project): Determines if the serialization callback methods are public (OnEntityDeserializing, OnEntityDeserialized, etc.)