I'm using Nlog to write some logging to a textfile. Partial nlog.config:
<target name="file" xsi:type="File" fileName="${basedir}/MBWRunner_log.txt"
layout="${date} (${level}): ${message}
Exception: ${exception:format=Method, ToString}"/>
Lines in the logfile look like this:
0001-01-01 00:00:00 (Trace): MBWRunner started
As you can see the date and time are all 0. I have tested {longdate} and {date:format=yyyyMMddHHmmss} with the same result.
The application is a console app, run from an elevated commandline.
Any clues?
[EDIT] I have tested this on 2 machine's within the organisation with the same result. Please help!
Code used:
static Logger _logger = LogManager.GetCurrentClassLogger();
public static void Log(string message, LogLevel priority)
{
LogEventInfo eventinfo = new LogEventInfo(); ;
eventinfo.Message = message;
eventinfo.Level = priority;
Log(eventinfo);
}
static void Log(LogEventInfo logentry)
{
_logger.Log(logentry);
}
UPDATE:
#edosoft I think the problem is your use of the default constructor for LogEventInfo. If you look at the source for LogEventInfo here
https://github.com/NLog/NLog/blob/master/src/NLog/LogEventInfo.cs
You will see that using the default constructor does not populate the .TimeStamp field, so the field will probably just default to the default value for DateTime, which I assume is DateTime.MinValue. You should use one of the other constructors or one of the Create methods. Since you are setting only the Message and Level fields, I would suggest either:
var logEvent = new LogEventInfo(priority, "", message); //Second param is logger name.
Or
var logEvent = LogEventInfo.Create(priority, "", message);
From the NLog source for DateLayoutRenderer (from here) we can see that the date value that gets written as part of the logging stream is calculated like this:
protected override void Append(StringBuilder builder, LogEventInfo logEvent)
{
var ts = logEvent.TimeStamp;
if (this.UniversalTime)
{
ts = ts.ToUniversalTime();
}
builder.Append(ts.ToString(this.Format, this.Culture));
}
What is happening here is that the DateLayoutRenderer is getting the TimeStamp value from the LogEventInfo object (NLog creates one of these each time you use the Logger.Trace, Logger.Debug, Logger.Info, etc methods. You can also create LogEventInfo objects yourself and log them with the Logger.Log method).
By default, when a LogEventInfo object is created, its TimeStamp field is set like this (from the source for LogEventInfo here) (note the use of CurrentTimeGetter.Now):
public LogEventInfo(LogLevel level, string loggerName, IFormatProvider formatProvider, [Localizable(false)] string message, object[] parameters, Exception exception)
{
this.TimeStamp = CurrentTimeGetter.Now;
this.Level = level;
this.LoggerName = loggerName;
this.Message = message;
this.Parameters = parameters;
this.FormatProvider = formatProvider;
this.Exception = exception;
this.SequenceID = Interlocked.Increment(ref globalSequenceId);
if (NeedToPreformatMessage(parameters))
{
this.CalcFormattedMessage();
}
}
The TimeStamp field is set in the LogEventInfo constructor using the TimeSource.Current.Now property, whose implementation can be seen here.
(UPDATE - At some point NLog changed from using CurrentTimeGetter to a more generic approach of having a TimeSource object that has several flavors (one of which, CachedTimeSource, is essentially the same as CurrentTimeGetter)).
To save the trouble of navigating the link, here is the source for CachedTimeSource:
public abstract class CachedTimeSource : TimeSource
{
private int lastTicks = -1;
private DateTime lastTime = DateTime.MinValue;
/// <summary>
/// Gets raw uncached time from derived time source.
/// </summary>
protected abstract DateTime FreshTime { get; }
/// <summary>
/// Gets current time cached for one system tick (15.6 milliseconds).
/// </summary>
public override DateTime Time
{
get
{
int tickCount = Environment.TickCount;
if (tickCount == lastTicks)
return lastTime;
else
{
DateTime time = FreshTime;
lastTicks = tickCount;
lastTime = time;
return time;
}
}
}
}
The purpose of this class is to use a relatively cheap operation (Environment.Ticks) to limit access to a relatively expensive operation (DateTime.Now). If the value of Ticks does not change from call to call (from one logged message to the next), then the value of DateTime.Now retrieved the this time will be the same as the value of DateTime.Now retrieved this time, so just use the last retrieved value.
With all of this code in play (and with Date/Time logging apparently working for most other people), one possible explanation of your problem is that you are using the Logger.Log method to log your messages and you are building the LogEventInfo objects yourself. By default, if you just new a LogEventInfo object, the automatic setting of the TimeStamp property should work fine. It is only dependent on Environment.Ticks, DateTime.Now, and the logic that reuses the last DateTime.Now value, if appropriate.
Is it possible that you are creating a LogEventInfo object and then setting its TimeStamp property to DateTime.MinValue? I ask because the date that is being logged is DateTime.MinValue.
The only other explanation that I can think of would be if Environment.Ticks returns -1 for some reason. If it did, then CurrentTimeGetter would always return the initial value of the lastDateTime private member variable. I can't imagine a scenario where Environment.Ticks would return -1.
Related
I've got a class that contains multiple potential sources of value (of same Type ofc) and property that returns value of some source based on provided sources (in order of importance, including internal value). I'd simply call it Provider, but property can be set also (and this actually sets some source value based on the same logic), so it looks incorrect.
I'd rather call it Two-Way Provider or Contractor (which is cumbersome), but it feels like I've got some pattern here, that has a specific or comprehensive name. More of "near-the-truth" variants: Dispenser, Conductor, Supply, Agent, Contributor. Something as simple as Node, but more specific.
tl;dr: Name must reflect all or most of these statements:
class contains multiple external value sources that can be set "outside" (Exposer???)
class contains "default" internal value (that can be "overriden" by source values) (Container?)
class (property) always provide some value (Provider?)
that (class's) property can be set (Receiver?)
that value, which can be get or set, comes either from external value source (if any), either from internal value (Resolver?)
looks logical next to the Type of value that class (property) presents (example: FloatProvider)
public class FloatProvider //Needs proper name
{
public float internalValue = 1.0f; // these may be renamed
public Foo fooSource = null; // to make more sense
public Bar barSource = null; // in terms of the class
public float Value
{
get => GetValue();
set => SetValue(value);
}
public float GetValue()
{
if (fooSource!=null) return fooSource.value;
else if (barSource!=null) return barSource.value;
else return internalValue;
}
public void SetValue(float value)
{
if (fooSource!=null) fooSource.value = value;
else if (barSource!=null) barSource.value = value;
else internalValue = value;
}
public FloatProvider(float internalValue, Foo fooSource, Bar barSource)
{
this.internalValue = internalValue;
this.fooSource = fooSource;
this.barSource = barSource;
}
}
public class Foo
{
public float value = 10.0f;
}
public class Bar
{
public float value = 0.1f;
}
After days of head scratching and eventual epiphanies I think the Hub is the most appropriate term. But in my case I'd rather call the class by its purpose (FloatExt or something).
Okay so I am making a custom feature for my OP-Prison server, one of the things that I need to do is get an integer from the players.yml file, check if it is >= one, if it is take away one, save it and then if it is still above one then they can repeat the action untill it's 0.
The issue comes with the fact that I have to restart the server for the file to change, and even when I do, it will only go down by one integer at a time, before having to reload it again.
GUI Creation code:
Main main = Main.getPlugin(Main.class);
#SuppressWarnings("unused")
private FileControl fc;
#SuppressWarnings("unused")
private FileControl playerfc;
public static String inventoryname = Utils.chat(Main.pl.getFileControl().getConfig().getString("Backpacks.White.InventoryName"));
public List<Player> WhiteOpened = new ArrayList<>();
public static Inventory whiteBackpack(Player player) {
Inventory whiteBackpack = Bukkit.createInventory(null, 27, (inventoryname));
UUID uuid = player.getUniqueId();
whiteBackpack.setItem(10,
new ItemCreator(Material.INK_SACK).setData(8)
.setDisplayname(Utils.chat("&fCommon Packages &8» &f&l" + Main.pl.getPlayerFile().getConfig().getInt("Users." + uuid + ".Packages.Common")))
.getItem());
return whiteBackpack;
}
Code for updating the config + item when the Commonpackage is clicked:
#EventHandler
public void whiteBackpackInteract(InventoryClickEvent event) {
Player player = (Player) event.getWhoClicked();
UUID uuid = player.getUniqueId();
ItemStack clicked = event.getCurrentItem();
String title = event.getInventory().getName();
if (title.equals(inventoryname)) {
// Making it so that the item cannot be moved
event.setCancelled(true);
if (clicked != null) {
if (event.getSlot() == 10) {
// Getting the user's common packages section in the config and checking if it is greater than or equal to 1.
if (Main.pl.getPlayerFile().getConfig().getInt("Users." + uuid + ".Packages.Common") >= 1) {
// Saving the user's common package section to 'currentCommon'
Integer currentCommon = Main.pl.getPlayerFile().getConfig().getInt("Users." + uuid + ".Packages.Common");
// Taking away one from 'currentCommon' and saving it to 'newCommon'
Integer newCommon = currentCommon - 1;
// Getting the 'players.yml' file
File file = new File(main.getDataFolder(), "players.yml");
FileConfiguration config = YamlConfiguration.loadConfiguration(file);
// Checking if the current common keys is greater than or equal to 1
if (currentCommon >= 1) {
try {
//Now, Here's where the error lies.
//Gets the player's common package count and sets it to the 'newCommon' count
config.set("Users." + uuid + ".Packages.Common", newCommon);
//Saves the players.yml file
config.save(file);
} catch (IOException e) {
e.printStackTrace();
}
// Updates the inventory they're currently in (Atleast it's meant to...)
player.updateInventory();
// Sends them a message (This is just for testing purposes, making sure it's working.)
player.sendMessage(Utils.chat("&8(&9Vexil&8) &fCommon Package"));
}
}
}
}
}
}
If there is any other code that you need, just ask I'll happily provide it for you.
Right now, you need to restart the server for it to save the data to the file. This should not happen, since you are calling the method config.save(file). The following is simply speculation, but it's the only cause that I think can easily explain what is going on.
In the object that is returned by getPlayerFile().getConfig(), there is likely a variable that stores a FileConfiguration object. That variable houses all the data from the players.yml file. In your whiteBackpackInteract() method, you load the data all over again. You then continue on to write to this NEW FileConfiguration variable, rather than the one that is stored in getPlayerfile().getConfig(). Since you then proceed to save to the file directly, the variables stored in the getPlayerfile().getConfig() is never told that you changed some values around. To fix this, you need to change the following:
config.set("Users." + uuid + ".Packages.Common", newCommon);
config.save(file);
to this:
Main.pl.getPlayerFile().getConfig().set("Users." + uuid + ".Packages.Common", newCommon);
Main.pl.getPlayerFile().getConfig().save(file);
and then delete this line of code:
FileConfiguration config = YamlConfiguration.loadConfiguration(file);
This should solve your problem entirely. If it does not, I would recommend not using your friend's custom config API and instead just use the ones that are built in. Using third party code that you don't properly understand can very often lead to problems such as this.
The following are not the bugs, but are suggestions to help improve your code:
You should be sure to put your comments ABOVE or to the RIGHT over the code they describe. People read from top to bottom, so the comments (before I made the suggested edit to your post) were all below the code they describe.
Typically, you want to try to make sure that if code doesn't need to be run, it isn't. Since the int newCommon is not used until inside that if statement, you should move it in there.
You are using Main.getPlugin();
Now while that doesn't seem like such a bad thing, your getting an unassigned variable, I have no idea how it is working but you're assigning Main to Main. There are 2 proper ways to actually get the main class.
The first, and generally best way, is to use dependency injection.
So basically,
public class Main extends JavaPlugin {
#Override
public void onEnable() {
BackpackListener listener new Backpacklistener(this);
getServer().getPluginManager().registerEvents(listener, this);
}
}
public class BackpackListener implements Listener {
private Main instance;
private BackpackUtil util;
public BackpackListener(Main instance) {
this.instance = instance;
util = new BackpackUtil();
}
#EventHandler
public void onClick(InventoryClickEvent event) {
//code
util.whiteBackpack(instance);
}
public class BackpackUtil {
public Inventory whiteBackpack(Main instance) {
FileConfiguration config = instance.getConfig();
//Do things
instance.saveConfig();
}
}
The next way you can do it is less optimal, and frowned upon, but still an easier option.
public class Main() {
public static Main instance;
#Override
public void onEnable() {
instance = this;
}
}
public class ConfigHelper() {
Main instance = Main.instance;
FileConfiguration config = instance.getConfig();
//Do things
instance.saveConfig();
}
It's good to get out of the habit of using the second method (It's called a singleton), because normally the main class will change, or have multiple instances, etc... but with Spigot there can only be one main instance and one thread.
I have a use case where:
A job can be of many types, says A, B and C.
A tool can be configured to be a type: A, B and C
A job can be assigned to a tool. The end time of the job depends on the current configured type of the tool. If the tool's current configured type is different from the type of the job, then time needs to be added to change the current tool configuration.
My #PlanningEntity is Allocation, with startTime and tool as #PlanningVariable. I tried to add the currentConfiguredToolType in the Allocation as the #CustomShadowVariable and update the toolType in the shadowListener's afterVariableChanged() method, so that I have the correct toolType for the next job assigned to the tool. However, it is giving me inconsistent results.
[EDIT]: I did some debugging to see if the toolType is set correctly. I found that the toolType is being set correctly in afterVariableChanged() method. However, when I looked at the next job assigned to the tool, I see that the toolType has not changed. Is it because of multiple threads executing this flow? One thread changing the toolType of the tool the first time and then a second thread simultaneously assigning the times the second time without taking into account the changes done by the first thread.
[EDIT]: I was using 6.3.0 Final earlier (till yesterday). I switched to 6.5.0 Final today. There too I am seeing similar results, where the toolType seems to be set properly in afterVariableChanged() method, but is not taken into account for the next allocation on that tool.
[EDIT]: Domain code looks something like below:
#PlanningEntity
public class Allocation {
private Job job;
// planning variables
private LocalDateTime startTime;
private Tool tool;
//shadow variable
private ToolType toolType;
private LocalDateTime endTime;
#PlanningVariable(valueRangeProviderRefs = TOOL_RANGE)
public Tool getTool() {
return this.tool;
}
#PlanningVariable(valueRangeProviderRefs = START_TIME_RANGE)
public LocalDateTime getStartTime() {
return this.startTime;
}
#CustomShadowVariable(variableListenerClass = ToolTypeVariableListener.class,
sources = {#CustomShadowVariable.Source(variableName = "tool")})
public ToolType getCurrentToolType() {
return this.toolType;
}
private void setToolType(ToolType type) {
this.toolType = type;
this.tool.setToolType(type);
}
private setStartTime(LocalDateTime startTime) {
this.startTime = startTime;
this.endTime = getTimeTakenForJob() + getTypeChangeTime();
...
}
private LocalDateTime getTypeChangeTime() {
//typeChangeTimeMap is available and is populated with data
return typeChangeTimeMap.get(tool.getType);
}
}
public class Tool {
...
private ToolType toolType;
getter and setter for this.
public void setToolType() { ...}
public ToolType getToolType() { ...}
}
public class ToolTypeVariableListener implements VariableListener<Allocation> {
...
#Override
public void afterVariableChanged(ScoreDirector scoreDirector, Allocation entity) {
scoreDirector.afterVariableChanged(entity, "currentToolType");
if (entity.getTool() != null && entity.getStartTime() != null) {
entity.setCurrentToolType(entity.getJob().getType());
}
scoreDirector.afterVariableChanged(entity, "currentToolType");
}
[EDIT]: When I did some debugging, looks like the toolType set in the machine for one allocation is used in calculating the type change time for a allocation belonging to a different evaluation set. Not sure how to avoid this.
If this is indeed the case, what is a good way to model problems like this where the state of a item affects the time taken? Or am I totally off. I guess i am totally lost here.
[EDIT]: This is not an issue with how Optaplanner is invoked, but score corruption, when the rule to penalize it based on endTime is added. More details in comments.
[EDIT]: I commented out the rules specified in rules one-by-one and saw that the score corruption occurs only when the score computed depends on the computed values: endTime and toolTypeChange. It is fine when the score depends on the startTime, which is a planningVariable alone. However, that does not give me the best results. It gives me a solution which has a negative hard score, which means it violated the rule of not assigning the same tool during the same time to different jobs.
Can computed values not be used for score calculations?
Any help or pointer is greatly appreciated.
best,
Alice
The ToolTypeVariableListener seems to lack class to the before/after methods, which can cause score corruption. Turn on FULL_ASSERT to verify.
I want to create input forms which validate user input and prevent the model from being saved with invalid data. I have been using databinding which works up to a point but my implementation is not as intuitive as I would like.
Imagine an input which contains '123' and the value must not be empty. The user deletes the characters one by one until empty. The databinding validator shows an error decoration.
However, if the user saves the form and reloads it, then a '1' is displayed in the field - i.e. the last valid input. The databinding does not transmit the invalid value into the model.
I have a ChangeListener but this is called before the databinding so at that point the invalid state has not been detected.
I would like the error to be displayed in the UI but the model remains valid (this is already so). Also, for as long as the UI contains errors, it should not be possible to save the model.
/**
* Bind a text control to a property in the view model
**/
protected Binding bindText(DataBindingContext ctx, Control control,
Object viewModel, String property, IValidator validator)
{
IObservableValue value = WidgetProperties.text(SWT.Modify).observe(
control);
IObservableValue modelValue = BeanProperties.value(
viewModel.getClass(), property).observe(viewModel);
Binding binding = ctx.bindValue(value, modelValue, getStrategy(validator), null);
binding.getTarget().addChangeListener(listener);
ControlDecorationSupport.create(binding, SWT.TOP | SWT.LEFT);
return binding;
}
private UpdateValueStrategy getStrategy(IValidator validator)
{
if (validator == null)
return null;
UpdateValueStrategy strategy = new UpdateValueStrategy();
strategy.setBeforeSetValidator(validator);
return strategy;
}
private IChangeListener listener = new IChangeListener()
{
#Override
public void handleChange(ChangeEvent event)
{
// notify all form listeners that something has changed
}
};
/**
* Called by form owner to check if the form contains valid data e.g. before saving
**/
public boolean isValid()
{
System.out.println("isValid");
for (Object o : getDataContext().getValidationStatusProviders())
{
ValidationStatusProvider vsp = (ValidationStatusProvider) o;
IStatus status = (IStatus)vsp.getValidationStatus()
.getValue();
if (status.matches(IStatus.ERROR))
return false;
}
return true;
}
Your best bet is to steer clear of ChangeListeners - as you've discovered, their order of execution is either undefined or just not helpful in this case.
Instead, you want to stick with the 'observable' as opposed to 'listener' model for as long as possible. As already mentioned, create an AggregateValidationStatus to listen to the overall state of the DataBindingContext, which has a similar effect to your existing code.
Then you can either listen directly to that (as below) to affect the save ability, or you could even bind it to another bean.
IObservableValue statusValue = new AggregateValidationStatus(dbc, AggregateValidationStatus. MAX_SEVERITY);
statusValue.addListener(new IValueChangeListener() {
handleValueChange(ValueChangeEvent event) {
// change ability to save here...
}
});
You can use AggregateValidationStatus to observe the aggregate validation status:
IObservableValue value = new AggregateValidationStatus(bindContext.getBindings(),
AggregateValidationStatus.MAX_SEVERITY);
You can bind this to something which accepts an IStatus parameter and it will be called each time the validation status changes.
Lets say I have an already functioning Play 2.0 framework based application in Scala that serves a URL such as:
http://localhost:9000/birthdays
which responds with a listing of all known birthdays
I now want to enhance this by adding the ability to restrict results with optional "from" (date) and "to" request params such as
http://localhost:9000/birthdays?from=20120131&to=20120229
(dates here interpreted as yyyyMMdd)
My question is how to handle the request param binding and interpretation in Play 2.0 with Scala, especially given that both of these params should be optional.
Should these parameters be somehow expressed in the "routes" specification? Alternatively, should the responding Controller method pick apart the params from the request object somehow? Is there another way to do this?
Encode your optional parameters as Option[String] (or Option[java.util.Date], but you’ll have to implement your own QueryStringBindable[Date]):
def birthdays(from: Option[String], to: Option[String]) = Action {
// …
}
And declare the following route:
GET /birthday controllers.Application.birthday(from: Option[String], to: Option[String])
A maybe less clean way of doing this for java users is setting defaults:
GET /users controllers.Application.users(max:java.lang.Integer ?= 50, page:java.lang.Integer ?= 0)
And in the controller
public static Result users(Integer max, Integer page) {...}
One more problem, you'll have to repeat the defaults whenever you link to your page in the template
#routes.Application.users(max = 50, page = 0)
In Addition to Julien's answer. If you don't want to include it in the routes file.
You can get this attribute in the controller method using RequestHeader
String from = request().getQueryString("from");
String to = request().getQueryString("to");
This will give you the desired request parameters, plus keep your routes file clean.
Here's Julien's example rewritten in java, using F.Option: (works as of play 2.1)
import play.libs.F.Option;
public static Result birthdays(Option<String> from, Option<String> to) {
// …
}
Route:
GET /birthday controllers.Application.birthday(from: play.libs.F.Option[String], to: play.libs.F.Option[String])
You can also just pick arbitrary query parameters out as strings (you have to do the type conversion yourself):
public static Result birthdays(Option<String> from, Option<String> to) {
String blarg = request().getQueryString("blarg"); // null if not in URL
// …
}
For optional Query parameters, you can do it this way
In routes file, declare API
GET /birthdays controllers.Application.method(from: Long, to: Long)
You can also give some default value, in case API doesn't contain these query params it will automatically assign the default values to these params
GET /birthdays controllers.Application.method(from: Long ?= 0, to: Long ?= 10)
In method written inside controller Application these params will have value null if no default values assigned else default values.
My way of doing this involves using a custom QueryStringBindable. This way I express parameters in routes as:
GET /birthdays/ controllers.Birthdays.getBirthdays(period: util.Period)
The code for Period looks like this.
public class Period implements QueryStringBindable<Period> {
public static final String PATTERN = "dd.MM.yyyy";
public Date start;
public Date end;
#Override
public F.Option<Period> bind(String key, Map<String, String[]> data) {
SimpleDateFormat sdf = new SimpleDateFormat(PATTERN);
try {
start = data.containsKey("startDate")?sdf.parse(data.get("startDate") [0]):null;
end = data.containsKey("endDate")?sdf.parse(data.get("endDate")[0]):null;
} catch (ParseException ignored) {
return F.Option.None();
}
return F.Option.Some(this);
}
#Override
public String unbind(String key) {
SimpleDateFormat sdf = new SimpleDateFormat(PATTERN);
return "startDate=" + sdf.format(start) + "&" + "endDate=" + sdf.format(end);
}
#Override
public String javascriptUnbind() {
return null;
}
public void applyDateFilter(ExpressionList el) {
if (this.start != null)
el.ge("eventDate", this.start);
if (this.end != null)
el.le("eventDate", new DateTime(this.end.getTime()).plusDays(1).toDate());
}
}
applyDateFilter is just a convienence method i use in my controllers if I want to apply date filtering to the query. Obviously you could use other date defaults here, or use some other default than null for start and end date in the bind method.